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

/*

Copyright (c) 2002-2008 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.

*/

/** \file
 *
 * \brief The implementation of utility functions
 *
 * This file declares the body of the functions which are not
 * inline. It is part of the SSWF library.
 */

#include	"sswf/libsswf.h"

using namespace sswf;


/* first define objects which have no implementation in any C++ file */


/** \def exposed_class
 *
 * \brief Defines an exposed class
 *
 * The SSWF library can be used in C++ and in C. All the classes which are exposed
 * to the C users are defined as 'exposed_class' instead of just 'class'.
 *
 * The "only" problem with the use of this exposed_class \#define is the fact that
 * Doxygen does not understand the default classes. So I have to replace it with
 * just 'class' and then the libsswf.h file looks like it comes from tmp/libsswf.h
 * instead of the include/sswf/libsswf.h...
 */


/** \def ICONV_INPUT_CAST
 *
 * \brief The second parameter of iconv() changes from system to system
 *
 * The iconv() call from FreeBSD & MacOS/X differs from the Linux & Windows versions.
 * One uses a 'const char **' and the other just a 'char **'. This is used to call
 * iconv() without having to have \#ifdef around each call.
 *
 * So, in one case the ICONV_INPUT_CAST is defined to nothing and in the other case
 * it is set the (char **).
 */


/** \def invisible
 *
 * \brief Renders the following members invisible
 *
 * This is a brand new C++ extension! In order to expose the C++ library to C people,
 * I needed to create a parser which would see everything which needs to be made
 * available in the C library.
 *
 * The problem is that a certain number of structures include functions. These cannot
 * easilly be offered to C programmers and thus I hid them (I marked them invisible.)
 *
 * In C++, this is 100% equivalent to public. For my parser, it is very much equivalent
 * to private.
 */


/** \def M_PI
 *
 * \brief A close representation of PI
 *
 * The Microsoft headers somehow do not offer M_PI.
 */


/** \def SSWF_ALIGN(value, modulo)
 *
 * \brief Align 'value' according to 'modulo'
 *
 * This function aligns the offset in 'value' to the next multiple of
 * 'module'. Modulo <b>MUST</b> be a power of 2 for this macro to work
 * properly. The macro does not check the validity of modulo.
 *
 * \sa sswf::Buffer
 * \sa SSWF_SPACE
 */


/** \def SSWF_EVENT_CONDITIONS
 *
 * \brief Defines all the condition flags available in DefineButton2
 *
 * This mask can be used to know whether flags for DefineButton2 are
 * used in a condition.
 *
 * \sa sswf::Event
 * \sa #SSWF_EVENT_V5
 * \sa #SSWF_EVENT_V6
 * \sa #SSWF_EVENT_V7
 */


/** \def SSWF_EVENT_COUNT
 *
 * \brief The total number of events supported by SWF
 *
 * This counter gives you the total number of events supported by SWF.
 *
 * All the events have a name which is defined in the
 * sswf::Event::f_event_names array. This \#define represents the
 * size of that array.
 *
 * \sa sswf::Event
 * \sa sswf::Event::f_event_names
 */


/** \def SSWF_EVENT_V5
 *
 * \brief The mask of events supported in version 5
 *
 * This definition represents all the events supported in version 5 of SWF.
 *
 * If no other bits are set, then you can save the tag in an SWF
 * version 5.
 *
 * \sa sswf::Event
 * \sa #SSWF_EVENT_V6
 * \sa #SSWF_EVENT_V7
 */


/** \def SSWF_EVENT_V6
 *
 * \brief The mask of events supported in version 6
 *
 * This definition represents all the events supported in version 6 of SWF.
 *
 * If only bits from SSWF_EVENT_V5 and SSWF_EVENT_V6 are set, then you can
 * save the tag in an SWF version 6.
 *
 * \sa sswf::Event
 * \sa #SSWF_EVENT_V5
 * \sa #SSWF_EVENT_V7
 */


/** \def SSWF_EVENT_V7
 *
 * \brief The mask of events supported in version 7
 *
 * This definition represents all the events supported in version 6 of SWF.
 *
 * If only bits from SSWF_EVENT_V5, SSWF_EVENT_V6 and SSWF_EVENT_V7 are set,
 * then you can save the tag in an SWF version 7.
 *
 * \sa sswf::Event
 * \sa #SSWF_EVENT_V5
 * \sa #SSWF_EVENT_V6
 */


/** \def SSWF_FONT_SPACE_INDEX
 *
 * \brief The index where the space is saved in a font.
 *
 * The space character is peculiar in a font definition since multiple
 * character can be "rendered" as a space (i.e. really, there is nothing
 * to render for a space.)
 *
 * Thus this special index is used in order to be capable of accepting
 * multiple definition of the space character.
 *
 * \sa sswf::TagFont
 */


/** \def SSWF_ID_NONE
 *
 * \brief The special identifier 'none'
 *
 * This value is used as the "undefined identifier" marker. This is used
 * primarily for glyphs (shapes) which are added to a font as characters.
 * These do not need an identifier.
 *
 * It can also be used for references which are not yet defined.
 *
 * \sa sswf::TagBaseID
 * \sa sswf::TagShape
 */


/** \def SSWF_KEY_COUNT
 *
 * \brief The number of special key codes
 *
 * The special keys that SWF understands are defined with their code and
 * name in the sswf::Event::f_key_names array. This \#define represents
 * the size of that array.
 *
 * \sa sswf::Event
 * \sa sswf::Event::f_key_names
 */


/** \def SSWF_SAFE
 *
 * \brief The size of the safe area around allocated memory buffers
 *
 * In debug mode, buffers are allocated with some extra safe guards
 * before and after. If these are written to, checking the block of
 * memory or trying to release the block of memory will generate an
 * assertion.
 *
 * In release, the safe area is set to 0 meaning that there is none
 * so watch out!
 *
 * \sa sswf::assert(int cond, const char *format, ...)
 */


/** \def SSWF_SPACE
 *
 * \brief Space reserved for the memory header blocks
 *
 * Any memory block allocated by the SSWF library has a header
 * defined by sswf::Buffer::mem_buffer_t. This size of this
 * header is such that you can allocated a buffer for double
 * values which are properly aligned. Note that will not work
 * properly for MMX memory access or long double.
 *
 * \sa #SSWF_ALIGN
 */


/** \name Tag types
 *
 * These flags define the type of the different tags available in the SSWF library.
 */

/*@{*/

/** \def SWF_TYPE_DEFINE
 *
 * \brief The bit defining a definition tag (i.e. sswf::TagSprite)
 *
 * When this bit is set, the tag represents a definition tag.
 *
 * Another bit defines control tags. When neither are set, it is
 * usually an SWF format tag.
 *
 * \sa #SWF_TYPE_CONTROL
 */

/** \def SWF_TYPE_CONTROL
 *
 * \brief The bit defining a control tag (i.e. sswf::TagPlace)
 *
 * When this bit is set, the tag represents a control tag.
 *
 * \sa #SWF_TYPE_DEFINE
 */

/** \def SWF_TYPE_UNIQUE
 *
 * \brief The bit defining a tag which should be unique in the entire movie
 *
 * Some tags, such as sswf::SetBackgroundColor, cannot be defined more
 * than once in the entire movie. This flag is set on such tags.
 *
 * \sa #SWF_TYPE_ONE
 */

/** \def SWF_TYPE_START	
 *
 * \brief The bit defining a tag which should appear early in the movie
 *
 * Some tags, such as the sswf::TagMetadata, should be defined early in
 * the movie (one of the first format tags.) This bit is set on such
 * tags. The order is unspecified and is usually not an issue.
 */

/** \def SWF_TYPE_ONE
 *
 * \brief The bit defining a tag which should appear once in an entire frame
 *
 * Tags marked with this bit represent tags which can be used at most once
 * within a frame (between two sswf::TagShowFrame tags).
 *
 * \sa #SWF_TYPE_UNIQUE
 */

/** \def SWF_TYPE_REFERENCE
 *
 * \brief The bit defining a tag which includes a reference to another tag
 *
 * This bit is set on tags which reference others (sswf::TagPlace).
 */

/** \def SWF_TYPE_HAS_ID
 *
 * \brief The bit defining a tag derived from sswf::TagBaseID
 *
 * This bit is set on all the tags which derive from sswf::TagBaseID.
 */

/** \def SWF_TYPE_SCRIPT
 *
 * \brief The bit defining a tag which supports scripts (i.e. sswf::TagDoAction)
 *
 * This flag is set in all the tags which can include an ActionScript.
 *
 * \sa sswf::TagDoAction
 * \sa sswf::TagButton
 * \sa sswf::TagPlace
 */

/** \def SWF_TYPE_SPRITE
 *
 * \brief Only sswf::TagSprite objects have this bit set
 *
 * At this time, only the sswf::TagSprite objects have this bit set.
 *
 * \sa #SWF_TYPE_HEADER
 */

/** \def SWF_TYPE_HEADER
 *
 * \brief Only the sswf::TagHeader has this bit set
 *
 * Only the sswf::TagHeader object has this bit set. It is used here and there
 * to know whether a tag is in a sprite or directly in the header.
 *
 * \sa #SWF_TYPE_SPRITE
 */

/*@}*/


/** \typedef unsigned short sswf::sswf_frame_t;
 *
 * \brief Used to represent a frame number
 *
 * This typedef is used anywhere a frame needs to be defined by number.
 * The very first frame in a movie is frame 0. The very last frame is
 * to the total number of frames minus one.
 *
 * Note that there are frames within a sswf::TagSprite. Similarly, the
 * very first frame starts at 0 and the last is the number of frames in
 * that sprite minus one.
 *
 * \sa sswf::TagHeader
 * \sa sswf::TagSprite
 * \sa sswf::ActionCallFrame
 */


/** \typedef unsigned short sswf::sswf_id_t;
 *
 * \brief Type defining the identifier of an SWF object
 *
 * Definitions in an SWF animation are given an identifier which can later
 * be used to reference the definition. For instance, you create a sswf::TagSprite,
 * assign it identifier 5, and later you display the sswf::TagSprite using
 * a sswf::TagPlace and referencing the sswf::TagSprite number 5.
 *
 * \sa class sswf::TagBaseID
 * \sa class sswf::TagSprite
 * \sa class sswf::TagPlace
 */


/** \typedef int32_t sswf::sswf_ucs4_t;
 *
 * \brief A type to support UCS-4 characters.
 *
 * This type is used to represent UCS-4 strings. The wchar_t cannot be used
 * since on some systems it is only 16 bits (and can therefore only represent
 * UCS-2 or UTF-16).
 *
 * The SSWF library supports functions to convert strings from UCS-4 to UTF-8
 * and vice versa.
 *
 * \sa int sswf::wctomb(const sswf_ucs4_t *wc, size_t wc_len, char *mb, size_t& mb_size)
 * \sa int sswf::mbtowc(const char *mb, size_t mb_len, sswf_ucs4_t *& wc, size_t& wc_len)
 * \sa char *sswf::wcname(sswf_ucs4_t wc, char *result)
 * \sa long sswf::wcslen(sswf_ucs4_t *wcstr)
 *
 * \sa typedef unsigned int wint_t;
 */


/** \typedef unsigned int wint_t;
 *
 * \brief This type is also used for wide characters.
 *
 * Some system functions require the use of the wint_t for wide characters.
 * However, the wint_t is not defined on all systems, so it is defined here
 * for that reason.
 *
 * Otherwise, wint_t is pretty much the same as sswf_ucs4_t.
 *
 * \sa typedef int32_t sswf::sswf_ucs4_t;
 */


/** \fn unsigned int sswf::swap_int(unsigned int l)
 *
 * \brief Swap the bytes of a 32 bits integer
 *
 * This function takes a 32 bits integer as input and swaps each byte.
 * In effect, it transforms the 32 bits integer from a little to a big
 * endian or vice versa.
 *
 * Calling the function again with the result value cancels the swapping
 * effect.
 *
 * \param l The 32 bits integer to swap
 *
 * \return The swapped 32 bits integer passed as input
 *
 * \sa unsigned short sswf::swap_short(unsigned short s)
 * \sa void sswf::swap(void *s1, void *s2, size_t size)
 */


/** \fn unsigned short sswf::swap_short(unsigned short s)
 *
 * \brief Swap the bytes of a 16 bits integer
 *
 * This function takes a 16 bits integer as input and swaps each byte.
 * In effect, it transforms the 16 bits integer from a little to a big
 * endian or vice versa.
 *
 * Calling the function again with the result cancels the swapping
 * effect.
 *
 * \param s The 16 bits integer to swap
 *
 * \return The swapped 16 bits integer passed as input
 *
 * \sa unsigned int sswf::swap_int(unsigned int l)
 * \sa void sswf::swap(void *s1, void *s2, size_t size)
 */
 

/** \fn void sswf::assert(int cond, const char *format, ...)
 *
 * \brief Stop the processing with a message about the test
 *
 * This function is used to assert() when 'cond' is true.
 *
 * The function prints out the message defined by format and
 * optional parameters (as in a printf(3) call) and then it
 * exists the process with a call to abort(3).
 *
 * The function only works in debug mode. In release mode (or non-debug at least)
 * it is transformed into a do nothing inline function.
 *
 * \param cond A true (i.e. do nothing) or false (abort the process) condition
 * \param format A format string as in a printf(3)
 * \param ... Optional parameter to the format string message
 */


/** \brief Swap the content of two buffers
 *
 * The swap() function swaps the content of buffer s1 with buffer s2.
 * 'size' bytes are swapped.
 *
 * If you have a 64 bits system, s1 and s2 both point to a 64 bit value
 * and the size is a multiple of 8 bytes, then the swap is done 64 bits
 * at a time.
 *
 * If s1 and s2 both point to a 32 bit value and the size is a multiple
 * of 4 bytes, then the swap is done 32 bits at a time.
 *
 * Otherwise the 8 bits (1 byte) at a time function is used.
 *
 * \note
 * Modifications to support 64-bit by Fabio A. Correa &lt;facorread at gmail&gt;
 *
 * \param s1 One of the buffers to swap
 * \param s2 One of the buffers to swap
 * \param size The number of bytes to swap
 *
 * \sa unsigned short sswf::swap_short(unsigned short s)
 * \sa unsigned int sswf::swap_int(unsigned int l)
 */
void sswf::swap(void *s1, void *s2, size_t size)
{
	if(size == 0) {
		return;
	}
	// on 64 bits systems, swap 64 bits at once
	if(sizeof(void *) == 8 && (size & 7) == 0 && (((intptr_t) s1) & 7) == 0 && (((intptr_t) s2) & 7) == 0) {
		int64_t	a;

		assert(sizeof(int64_t) == 8, "the swap() function assumes that the sizeof(int64_t) == 8...\n");
		do {
			a = * (int64_t *) s1;
			* (int64_t *) s1 = * (int64_t *) s2;
			* (int64_t *) s2 = a;
			s1 = (int64_t *) s1 + 1;
			s2 = (int64_t *) s2 + 1;
			size -= sizeof(int64_t);
		} while(size > 0);
	}
	else if((size & 3) == 0 && (((intptr_t) s1) & 3) == 0 && (((intptr_t) s2) & 3) == 0) {
		int32_t	a;

		assert(sizeof(int32_t) == 4, "the swap() function assumes that the sizeof(int32_t) == 4...\n");
		do {
			a = * (int32_t *) s1;
			* (int32_t *) s1 = * (int32_t *) s2;
			* (int32_t *) s2 = a;
			s1 = (int32_t *) s1 + 1;
			s2 = (int32_t *) s2 + 1;
			size -= sizeof(int32_t);
		} while(size > 0);
	}
	else {
		char	a;

		do {
			a = * (char *) s1;
			* (char *) s1 = * (char *) s2;
			* (char *) s2 = a;
			s1 = (char *) s1 + 1;
			s2 = (char *) s2 + 1;
			size -= sizeof(char);
		} while(size > 0);
	}
}


/** \brief Convert a wide character string to a multi-byte string
 *
 * This function converts a UCS-4 string to a UTF-8 string.
 *
 * Since version 6 of SWF, all strings in an SWF animations are assumed
 * to be encoding using UTF-8.
 *
 * The length of the output can be infered using 'mb_len' which is
 * decremented by the number of bytes saved in 'mb'.
 *
 * \param wc A pointer to a wide character string (input)
 * \param wc_len The number of characters to convert
 * \param mb A pointer to the output string (output)
 * \param mb_len A reference to a size_t value with the maximum number
 * 		of bytes to write in this output buffer (in/out)
 *
 * \return Zero when the function succeeds, EINVAL when an input wide
 *		character is erroneous (i.e. negative) and ENOMEM when
 *		the output buffer is full
 *
 * \sa int sswf::mbtowc(const char *mb, size_t mb_len, sswf_ucs4_t *& wc, size_t& wc_len)
 * \sa long sswf::wcslen(sswf_ucs4_t *wcstr)
 */
int sswf::wctomb(const sswf_ucs4_t *wc, size_t wc_len, char *mb, size_t& mb_len)
{
	char		buf[6];
	size_t		l;
	sswf_ucs4_t	c;

	while(wc_len >= sizeof(sswf_ucs4_t)) {
		/* get one wide character */
		c = *wc++;
		wc_len -= sizeof(sswf_ucs4_t);
		/* encode in temp buffer */
		if(c < 0x80) {
			/* this will also encode '\0'... */
			buf[0] = (char) c;
			l = 1;
		}
		else if(c < 0x800) {
			buf[0] = (c >> 6) | 0xC0;
			buf[1] = (c & 0x3F) | 0x80;
			l = 2;
		}
		else if(c < 0x10000) {
			buf[0] = (c >> 12) | 0xE0;
			buf[1] = ((c >> 6) & 0x3F) | 0x80;
			buf[2] = (c & 0x3F) | 0x80;
			l = 3;
		}
		else if(c < 0x200000) {
			buf[0] = (c >> 18) | 0xF0;
			buf[1] = ((c >> 12) & 0x3F) | 0x80;
			buf[2] = ((c >> 6) & 0x3F) | 0x80;
			buf[3] = (c & 0x3F) | 0x80;
			l = 4;
		}
		else if(c < 0x4000000) {
			buf[0] = (c >> 24) | 0xF8;
			buf[1] = ((c >> 18) & 0x3F) | 0x80;
			buf[2] = ((c >> 12) & 0x3F) | 0x80;
			buf[3] = ((c >> 6) & 0x3F) | 0x80;
			buf[4] = (c & 0x3F) | 0x80;
			l = 5;
		}
		else if(c > 0) {	// <=> (signed long) c < 0x80000000
			buf[0] = (c >> 30) | 0xFC;
			buf[1] = ((c >> 24) & 0x3F) | 0x80;
			buf[2] = ((c >> 18) & 0x3F) | 0x80;
			buf[3] = ((c >> 12) & 0x3F) | 0x80;
			buf[4] = ((c >> 6) & 0x3F) | 0x80;
			buf[5] = (c & 0x3F) | 0x80;
			l = 6;
		}
		else {
			/* an invalid wide character (negative!) */
			return errno = EINVAL;
		}
		if(l < mb_len) {
			mb_len -= l;
			memcpy(mb, buf, l);
			mb += l;
		}
		else {
			/* overflow error */
			return errno = ENOMEM;
		}
	}

	return 0;
}


/** \brief Transform a multi-byte string in a wide character string
 *
 * This function transforms a UTF-8 string in a UCS-4 string.
 *
 * The function moves the 'wc' pointer to the end of the string
 * and decrements the 'wc_len' parameter for each character added
 * to the output string.
 *
 * \param mb The UTF-8 string to convert to UCS-4
 * \param mb_len The length of the UTF-8 string
 * \param wc A reference to a pointer to save the result
 * \param wc_len A reference to the size of the wc buffer
 *
 * \return Zero when no error occurs, EINVAL when an invalid input
 * character is found (i.e. 0xFE or 0xFF) or ENOMEM when the
 * output buffer is full before the convertion ends
 *
 * \sa int sswf::wctomb(const sswf_ucs4_t *wc, size_t wc_len, char *mb, size_t& mb_len)
 * \sa long sswf::wcslen(sswf_ucs4_t *wcstr)
 */
int sswf::mbtowc(const char *mb, size_t mb_len, sswf_ucs4_t *& wc, size_t& wc_len)
{
	unsigned char	c;
	sswf_ucs4_t	w;
	size_t		l;

	while(mb_len > 0) {
		mb_len--;
		c = (unsigned char) *mb++;
		if(c < 0x80) {
			w = c;
		}
		else {
			if(c >= 0xC0 && c <= 0xDF) {
				l = 1;
				w = c & 0x1F;
			}
			else if(c >= 0xE0 && c <= 0xEF) {
				l = 2;
				w = c & 0x0F;
			}
			else if(c >= 0xF0 && c <= 0xF7) {
				l = 3;
				w = c & 0x07;
			}
			else if(c >= 0xF8 && c <= 0xFB) {
				l = 4;
				w = c & 0x03;
			}
			else if(c >= 0xFC && c <= 0xFD) {
				l = 5;
				w = c & 0x01;
			}
			else {
				return errno = EINVAL;
			}
			if(mb_len < l) {
				return errno = EINVAL;
			}
			mb_len -= l;
			while(l > 0) {
				c = (unsigned char) *mb++;
				if(c < 0x80 || c > 0xBF) {
					return errno = EINVAL;
				}
				l--;
				w = (w << 6) | (c & 0x3F);
			}
		}
		if(wc_len < sizeof(sswf_ucs4_t)) {
			return errno = ENOMEM;
		}
		wc_len -= sizeof(sswf_ucs4_t);
		*wc++ = w;
	}

	return 0;
}



/** \brief Transform a wide character in a display name
 *
 * This function transforms the given wide character in a string
 * with a name one can display in a console or UI output.
 *
 * A character between ' ' and 0x7E (127) are displayed as their
 * ASCII character. Any other character is displayed as 'U+&lt;number&gt;'
 *
 * \bug
 * 	This function takes a string where the result is saved without
 * 	any safeguards (i.e. it uses sprintf() on that buffer).
 *
 * \param wc The character to transform
 * \param result A pointer to a string to fill with the result (output)
 *
 * \return The function returns 'result'.
 *
 * \sa typedef int32_t sswf_ucs4_t
 */
char *sswf::wcname(sswf_ucs4_t wc, char *result)
{
	if(wc >= ' ' && wc <= 0x7E) {
		sprintf(result, "'%c' (%ld)", (char) wc, (long) wc);
	}
	else {
		sprintf(result, "'U+%04lX'", (long) wc);
	}

	return result;
}


/** \brief Compute the length of a wide character string
 *
 * This function computes the length, in character, of a wide character
 * string (i.e. UCS-4 characters.)
 *
 * The length does not include the null terminator.
 *
 * \param wcstr The string to size or NULL
 *
 * \return The number of character in the string
 *
 * \sa int sswf::mbtowc(const char *mb, size_t mb_len, sswf_ucs4_t *& wc, size_t& wc_len)
 * \sa int sswf::wctomb(const sswf_ucs4_t *wc, size_t wc_len, char *mb, size_t& mb_len)
 */
long sswf::wcslen(sswf_ucs4_t *wcstr)
{
	sswf_ucs4_t	*s;

	if(wcstr == 0) {
		return 0;
	}

	s = wcstr;
	while(*s != '\0') {
		s++;
	}

	return s - wcstr;
}


/** \brief Return the version of the SSWF library
 *
 * This function returns SSWF_VERSION as a string. (i.e. something like 1.7.5)
 *
 * This string is a constant and cannot be written to.
 *
 * \return The SSWF version string
 */
const char *sswf::sswf_version(void)
{
	static const char *version = TO_STR(SSWF_VERSION);
	return version;
}




/* The following options fold the documentation; use 'zi' to turn on and off
 *
 * vim: foldexpr=getline(v\:lnum)!~'^/\\*\\*'&&getline(v\:lnum)!~'^\ \\*'?0\:1 foldcolumn=2 foldmethod=expr
 */
