//	Zinc Application Framework - Z_STR.CPP
//	COPYRIGHT (C) 1990-1999.  All Rights Reserved.
//	Zinc Software Incorporated.  Pleasant Grove, Utah  USA

#include <z_str.hpp>
#include <z_ctype.hpp>
#include <z_stdarg.hpp>
#include <z_string.hpp>
#include <z_cset.hpp>
#include <z_lang.hpp>
#include <z_stdio.hpp>
#include <z_utils.hpp>
#define ZAF_STRING_DATA_INFO
#include "gbl_def.cpp"

const int NEWLINE_INCREMENT = 10;

// ----- ZafStringData ------------------------------------------------------

ZafStringData::ZafStringData(bool tStaticData) :
	ZafFormatData(), staticData(tStaticData)
{
	// Check the printf binding.
	Bind();

	// Initialize the string information.
	maxLength = -1;
	if (!staticData)
	{
		bufferSize = ZAF_STRING_DATA_LENGTH;
		value = new ZafIChar[bufferSize];
		strcpy(value, ZafLanguageManager::blankString);
	}
	else
	{
		bufferSize = 0;
		value = ZAF_NULLP(ZafIChar);
	}
}

ZafStringData::ZafStringData(const ZafIChar *zString, int zMaxLength, bool tStaticData) :
	ZafFormatData(), staticData(tStaticData), maxLength(zMaxLength)
{
	// Check the printf binding.
	Bind();

	// Initialize the string information.
	if (maxLength == -1)
		bufferSize = (int)(zString ? strlen(zString) + NEWLINE_INCREMENT + 1 : ZAF_STRING_DATA_LENGTH);
	else
		bufferSize = maxLength + 1;

	// Initialize value.
	if (!staticData)
	{
		value = new ZafIChar[bufferSize];
		strncpy(value, zString ? zString : ZafLanguageManager::blankString,
			(size_t)(bufferSize - 1));
		value[bufferSize - 1] = 0;
	}
	else
		value = (ZafIChar *)zString;
}

#if defined(ZAF_UNICODE)
ZafStringData::ZafStringData(const char *zString, int zMaxLength, bool tStaticData) :
	ZafFormatData(), staticData(tStaticData), maxLength(zMaxLength)
{
	// Check the printf binding.
	Bind();

	// Initialize the string information.
	if (maxLength == -1)
		bufferSize = (int)(zString ? strlen(zString) + NEWLINE_INCREMENT + 1 : ZAF_STRING_DATA_LENGTH);
	else
		bufferSize = maxLength + 1;

	// Initialize value.
	if (!staticData)
	{
		ZafIChar *tValue = ZafConvertToZafString(zString ? zString : "");
		value = new ZafIChar[bufferSize];
		strncpy(value, tValue, (size_t)(bufferSize - 1));
		value[bufferSize - 1] = 0;
		delete []tValue;
	}
	else
		value = (ZafIChar *)zString;
}
#endif

ZafStringData::ZafStringData(const ZafStringData &copy) :
	ZafFormatData(copy), staticData(copy.staticData), bufferSize(0), maxLength(-1),
	value(ZAF_NULLP(ZafIChar))
{
	// Initialize the string information.
	SetText(copy);
}

ZafStringData::~ZafStringData(void)
{
	// Delete the string information.
	if (value && !staticData)
		delete []value;
}

ZafStringData &ZafStringData::Append(const ZafIChar *string)
{
	if (string && *string)
	{
		// Append the substring.
		int length = (int)((value ? strlen(value) : 0) + strlen(string));

		// We can give the same behavior for staticData objects as
		// for regular objects (we truncate in both cases).
		ZafIChar *newText = new ZafIChar[length + 1];
		strcpy(newText, value ? value : ZafLanguageManager::blankString);
		strcat(newText, string);
		if (staticData)
		{
			strncpy(value, newText, (size_t)maxLength);
			SetText(value);
		}
		else
			SetText(newText);
		delete [] newText;
	}

	// Return the class reference.
	return (*this);
}

void ZafStringData::Bind(void)
{
	// Bind in the sprintf characters.
#if defined(ZAF_INDEPENDENT)
#	define bound zafStringData_bound
	extern bool bound;
#else
	static bool bound = false;
#endif
	if (!bound)
	{
		// Check for a valid locale.
		if (!zafLocale)
			ZafI18nData::I18nAllocate();

		// Set the lookup characters.
		bound = true;
/* START BLOCK COMMENT
**			ZafStandardArg::SetSprintf('c', ZafStringData::Format);
**			ZafStandardArg::SetSprintf('s', ZafStringData::Format);
**			ZafStandardArg::SetSscanf('s', ZafStringData::Parse);
**			ZafStandardArg::SetSscanf('c', ZafStringData::Parse);
**			ZafStandardArg::SetSscanf('[', ZafStringData::Parse);
END BLOCK COMMENT */
		ZafFormatData::SetSprintf('c', ZafStringData::Format);
		ZafFormatData::SetSprintf('s', ZafStringData::Format);
		ZafFormatData::SetSscanf('s', ZafStringData::Parse);
		ZafFormatData::SetSscanf('c', ZafStringData::Parse);
		ZafFormatData::SetSscanf('[', ZafStringData::Parse);
	}
}

void ZafStringData::Clear(void)
{
	// Reset the value.
	PushLevel();
	value[0] = '\0';
	SetError(ZAF_ERROR_NONE);
	PopLevel();
}

int ZafStringData::Compare(const ZafIChar *text, bool caselessCompare) const
{
	return (caselessCompare ? stricmp(value, text) : strcmp(value, text));
}

ZafData *ZafStringData::Duplicate(void)
{
	return (new ZafStringData(*this));
}

char *ZafStringData::DynamicOSText(void) const
{
	// Convert to an OS string (include room for NULL).
	char *osText = new char[((maxLength > 0) ? maxLength : bufferSize) * sizeof(ZafIChar) + sizeof(ZafIChar)];
	ZafConvertToOSString(value, osText, false);

#	if defined(ZAF_MOTIF)
	ZafCrNlToNl(osText);
#	elif defined(ZAF_MACINTOSH)
	ZafCrNlToCr(osText);
#	endif

	return (osText);
}

#if defined(ZAF_UNICODE)
wchar_t *ZafStringData::DynamicOSWText(void) const
{
	// Convert to an OS string.
	wchar_t *osText = new wchar_t[((maxLength > 0) ? maxLength : bufferSize)];
	ZafConvertToOSWString(value, osText, false);

#	if defined(ZAF_MOTIF)
	ZafCrNlToNl(osText);
#	elif defined(ZAF_MACINTOSH)
	ZafCrNlToCr(osText);
#	endif

	return (osText);
}
#endif

ZafIChar *ZafStringData::DynamicText(void) const
{
	// Duplicate the string data.
	ZafIChar *text = new ZafIChar[bufferSize];
	strcpy(text, value);
	return (text);
}

int ZafStringData::FormattedText(ZafIChar *buffer, int newBufferSize, const ZafIChar *newFormat) const
{
	// Format the text.
	return (Sprintf(buffer, newBufferSize, newFormat));
}

ZafStringData &ZafStringData::Insert(int offset, const ZafIChar *text, int length)
{
	// Insert the substring.
	PushLevel();
	if (text && *text)
	{
		if (length == -1)
			length = (int)strlen(text);

		int newLength = (int)(strlen(value) + length);
		bool growBuffer = newLength > bufferSize - 1;

		if (offset > bufferSize)
			offset = bufferSize;

		// If staticData, can't grow the buffer.
		if (staticData && growBuffer)
			return (*this);

		ZafIChar *target = value;

		// Grow the buffer if necessary.
		if (growBuffer)
		{
			int buffSize = (int)(strlen(value) + length + NEWLINE_INCREMENT + 1);
			ZafIChar *newStr = new ZafIChar[buffSize];
			strncpy(newStr, value, (size_t)offset);
			target = newStr;
		}

		// Move the characters after the inserted text.
		for (int size = (int)strlen(value); size >= offset; size--)
			target[size + length] = value[size];

		// Insert the text.
		for ( ; length; length--)
			target[offset++] = *text++;

		if (growBuffer)
		{
			SetText(target);
			delete []target;
		}
	}
	PopLevel();

	// Return the class reference.
	return (*this);
}

bool ZafStringData::IsA(ZafClassID compareID) const
{
	return ((compareID == classID) ? true : ZafFormatData::IsA(compareID));
}

bool ZafStringData::IsA(ZafClassName compareName) const
{
	return (!strcmp(compareName, className) ? true : ZafFormatData::IsA(compareName));
}

int ZafStringData::Length(void) const
{
	// Compute the current text length.
	int length = 0;
	if (value)
	{
		for (ZafIChar *temp = value; *temp; temp++)
			length++;
	}
	return (length);
}

ZafStringData &ZafStringData::Remove(int offset, int size)
{
	if (offset > bufferSize)
		return (*this);
	if (offset + size > bufferSize)
		size = bufferSize - offset - 1;

	// Remove the substring.
	PushLevel();
	memmove(&value[offset], &value[offset+size], (Length() - (offset + size) + 1) * sizeof(ZafIChar));
	PopLevel();

	// Return the class reference.
	return (*this);
}

ZafStringData &ZafStringData::Remove(const ZafIChar *text)
{
	// Remove the substring.
	ZafIChar *substring = strstr(value, text);
	if (substring)
	{
		PushLevel();
		ZafIChar *temp = &substring[strlen(text)];
		while (*temp)
			*substring++ = *temp++;
		*substring = '\0';
		PopLevel();
	}

	// Return the class reference.
	return (*this);
}

ZafStringData &ZafStringData::Replace(int offset, int size, const ZafIChar *text, int length)
{
	// Replace the substring.
	PushLevel();
	Remove(offset, size);
	Insert(offset, text, length);
	PopLevel();

	// Return the class reference.
	return (*this);
}

ZafError ZafStringData::SetChar(int offset, ZafIChar charValue, bool ignoreNotification)
{
	// Check for out-of-range.
	if (offset < 0 || offset > bufferSize - 1)
	{
		// Check for auto-sized buffer.
		if (maxLength != -1 || staticData)
			return (ZAF_ERROR_OUT_OF_RANGE);

		// Reconfigure the buffer.
		bufferSize = (offset < ZAF_STRING_DATA_LENGTH) ? ZAF_STRING_DATA_LENGTH : offset + ZAF_STRING_DATA_LENGTH;
		ZafIChar *temp = new ZafIChar[bufferSize];
		strcpy(temp, value);
		delete []value;
		value = temp;
	}

	// Reset the character.
	if (ignoreNotification)
		value[offset] = charValue;
	else
	{
		PushLevel();
		value[offset] = charValue;
		PopLevel();
	}

	// Return the error.
	return (ZAF_ERROR_NONE);
}

int ZafStringData::SetMaxLength(int newMaxLength)
{
	// Reset the length by calling SetText.
	maxLength = newMaxLength;
	if (staticData)
		bufferSize = maxLength + NEWLINE_INCREMENT + 1;
	if (!staticData && maxLength > bufferSize - 1)
	{
		bufferSize = maxLength + 1;
		ZafIChar *tempValue = new ZafIChar[bufferSize];
		if (value)
		{
			strcpy(tempValue, value);
			delete []value;
		}
		value = tempValue;
	}
	if (maxLength >= 0)
		value[maxLength] = '\0';
	return (maxLength);
}

bool ZafStringData::SetStaticData(bool tStaticData)
{
	// If the data is marked static, the user is in charge of protecting 
	// the data.  Otherwise, we must copy the contents to a new array.
	// This is done by re-setting the variables and data pointer, then
	// by resetting the array.
	if (staticData != tStaticData)
	{
		if (tStaticData)
			staticData = tStaticData;
		else
		{
			staticData = tStaticData;
			ZafIChar *newValue = value;
			value = new ZafIChar[bufferSize];
			strcpy(value, newValue ? newValue : ZafLanguageManager::blankString);
		}
	}
	return (staticData);
}

ZafError ZafStringData::SetOSText(const char *osText)
{
	// Convert the OS string.
	PushLevel();
#if defined(ZAF_MOTIF) || defined(ZAF_MACINTOSH)
	ZafIChar *newValue = new ZafIChar[2 * strlen(osText) + 1];
	ZafConvertToZafString(osText, newValue, false);
#else
	ZafIChar *newValue = ZafConvertToZafString(osText);
#endif

#	if defined(ZAF_MOTIF)
	ZafNlToCrNl(newValue);
#	elif defined(ZAF_MACINTOSH)
	ZafCrToCrNl(newValue);
#	endif

	SetText(newValue);
	delete []newValue;
	PopLevel();

	// Return success.
	return (ZAF_ERROR_NONE);
}

#if defined(ZAF_UNICODE)
ZafError ZafStringData::SetOSWText(const wchar_t *osText)
{
	// Convert the OS string.
	PushLevel();
#	if defined(ZAF_MOTIF) || defined(ZAF_MACINTOSH)
	ZafIChar *newValue = new ZafIChar[2 * strlen(osText) + 1];
	ZafConvertToZafString(osText, newValue, false);
#	else
	ZafIChar *newValue = ZafConvertToZafString(osText);
#	endif

#	if defined(ZAF_MOTIF)
	ZafNlToCrNl(newValue);
#	elif defined(ZAF_MACINTOSH)
	ZafCrToCrNl(newValue);
#	endif

	SetText(newValue);
	delete []newValue;
	PopLevel();

	// Return success.
	return (ZAF_ERROR_NONE);
}
#endif

ZafError ZafStringData::SetText(const ZafIChar *text) 
{ 
	return SetText(text, maxLength); 
}

ZafError ZafStringData::SetText(const ZafIChar *zString, int zMaxLength)
{
	// Reset the string value.
	PushLevel();
	SetMaxLength(zMaxLength);
	ZafIChar const *pString;
	if (zString)
		pString = zString;
	else
		pString = ZafLanguageManager::blankString;

	if (staticData)
	{
		if (value && zString)
			strcpy(value, pString);
		else
			value = (ZafIChar *)zString;
	}
	else if (maxLength == -1 && zString && (int)strlen(zString) + 1 > bufferSize)
	{
		if (value)
			delete []value;
		bufferSize = (int)(strlen(zString) + NEWLINE_INCREMENT + 1);
		value = new ZafIChar[bufferSize];
		strcpy(value, zString);
	}
	else if (maxLength >= 0)
	{
		strncpy(value, pString, (size_t)maxLength);
		value[maxLength] = '\0';
	}
	else if (value)	// zString is NULL
		strcpy(value, pString);

	PopLevel();

	// Return success.
	return (ZAF_ERROR_NONE);
}

ZafError ZafStringData::SetText(const ZafStringData &string)
{
	// Reset the string information.
	return SetText(string.value, string.maxLength);
}

ZafError ZafStringData::SetText(const ZafIChar *buffer, const ZafIChar *format)
{
	// Reset the value.
	PushLevel();
	Sscanf(buffer, format);
	PopLevel();

	// Return success.
	return (ZAF_ERROR_NONE);
}

// ----- operators ----------------------------------------------------------

/* START BLOCK COMMENT
**	ZafStringData operator+(const ZafStringData &string1, const ZafStringData &string2)
**	{
**		ZafStringData tmp = string1;
**		tmp.Append(string2);
**		return (tmp);
**	}
**	
**	ZafStringData operator+(const ZafStringData &string, const ZafIChar *value)
**	{
**		ZafStringData tmp = string;
**		tmp.Append(value);
**		return (tmp);
**	}
**	
**	ZafStringData operator+(const ZafIChar *value, const ZafStringData &string)
**	{
**		ZafStringData tmp(value);
**		tmp.Append(string);
**		return (tmp);
**	}
**	
**	ZafStringData operator+(const ZafStringData &string, const ZafIChar value)
**	{
**		ZafIChar string1[2];
**		string1[0] = value;
**		string1[1] = 0;
**		ZafStringData tmp = string;
**		tmp.Append(string1);
**		return (tmp);
**	}
**	
**	ZafStringData operator+(const ZafIChar value, const ZafStringData &string)
**	{
**		ZafIChar string1[2];
**		string1[0] = value;
**		string1[1] = 0;
**		ZafStringData tmp(string1);
**		tmp.Append(string);
**		return (tmp);
**	}
END BLOCK COMMENT */

ZafStringData &ZafStringData::operator+=(const ZafIChar tValue)
{
	ZafIChar string[2];
	string[0] = tValue;
	string[1] = 0;
	Append(string);
	return (*this);
}

/* START BLOCK COMMENT
**	ZafStringData operator-(const ZafStringData &string1, const ZafStringData &string2)
**	{
**		ZafStringData tmp = string1;
**		tmp.Remove(string2);
**		return (tmp);
**	}
**	
**	ZafStringData operator-(const ZafStringData &string, const ZafIChar *value)
**	{
**		ZafStringData tmp = string;
**		tmp.Remove(value);
**		return (tmp);
**	}
**	
**	ZafStringData operator-(const ZafIChar *value, const ZafStringData &string)
**	{
**		ZafStringData tmp = value;
**		tmp.Remove(string);
**		return (tmp);
**	}
END BLOCK COMMENT */

ZafStringData &ZafStringData::operator=(const ZafStringData &string)
{
	SetText(string);
	return (*this);
}

ZafStringData &ZafStringData::operator=(const ZafIChar *string)
{
	// Copy the string.
	SetText(string, -1);
	return (*this);
}

ZafStringData &ZafStringData::operator=(const ZafIChar character)
{
	// Copy the character.
	ZafIChar tString[2];
	tString[0] = character;
	tString[1] = 0;
	SetText(tString, -1);
	return (*this);
}

// ----- sprintf/sscanf functions -------------------------------------------

//	The rest of this file is modified from a version of vfprintf.c obtained
//	from the Berkeley Source Tree administered by The Regents of the
//	University of California (the "Regents")
//
//	Modifications made by Zinc are:
//	  COPYRIGHT (C) 1994-1999.  All Rights Reserved.
//	  Zinc Software Incorporated.  Pleasant Grove, Utah  USA
//
//	Permission from the Regents is conditioned on inclusion of the following
//	notice from the Regents which applies only to vfprintf.c:
//
//	  Copyright (c) 1990 The Regents of the University of California.
//	  All rights reserved.
//
//	  This code is derived from software contributed to Berkeley by
//	  Chris Torek.
//
//	  Redistribution and use in source and binary forms, with or without
//	  modification, are permitted provided that the following conditions
//	  are met:
//	  1. Redistributions of source code must retain the above copyright
//	     notice, this list of conditions and the following disclaimer.
//	  2. Redistributions in binary form must reproduce the above copyright
//	     notice, this list of conditions and the following disclaimer in the
//	     documentation and/or other materials provided with the distribution.
//	  3. All advertising materials mentioning features or use of this software
//	     must display the following acknowledgement:
//	       This product includes software developed by the University of
//	       California, Berkeley and its contributors.
//	  4. Neither the name of the University nor the names of its contributors
//	     may be used to endorse or promote products derived from this software
//	     without specific prior written permission.
//
//	  THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
//	  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
//	  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
//	  ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
//	  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
//	  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
//	  OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
//	  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
//	  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
//	  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
//	  SUCH DAMAGE.

//-----------------------------------------------------------------------------
#define	BUF		4
#define	PRINT(ptr, len) strncpy(buffer, ptr, (size_t)len), buffer += len;
#define	PAD(howmany, with) { \
	n = (howmany); \
	while (n > 0) { \
		*(buffer)++ = with; \
		n--; \
	} \
}

static ZafIChar _nullStr[] = { '(', 'n', 'u', 'l', 'l', ')', 0 };

void ZafStringData::Format(va_list *argList, ZafIChar **ptrBuffer,
	ZafIChar fmtch, int flags, int width, int prec)
{
	ZafIChar *buffer = *ptrBuffer;
	ZafIChar buf[BUF];
	ZafIChar *cp = buf;
	if (flags & ZAF_OBJ)
	{
		ZafStringData *tmp = va_arg(*argList, ZafStringData *);
		cp = tmp->value;
		if (!cp)
			return;
	}
	int size = 0;
	switch (fmtch)
	{
	case ' ':
		if (!(flags & ZAF_OBJ))
			*cp = buffer[0];
		size = 1;
		break;
	case 'c':
		if (!(flags & ZAF_OBJ))
		{
#if defined(__MWERKS__) && (__MWERKS__ <= 0x0a00)
			// NOTE: Metrowerks CodeWarrior 10 and earlier puts multiples of 4 bytes on the
			// stack, but remove the correct number of bytes. We have to account for this
			// bug when getting arguments of types less than 4 bytes in size by getting the
			// full 4 bytes that were put on the stack, and keeping what we need out of it.
			if (sizeof(ZafIChar) < 4)
				*cp = va_arg(*argList, long);
			else
				*cp = va_arg(*argList, ZafIChar);
#else
			*cp = (ZafIChar)va_arg(*argList, int);
#endif
		}
		size = 1;
		break;
	case 's':
		if (!(flags & ZAF_OBJ) && 
		 ((cp = va_arg(*argList, ZafIChar *)) == ZAF_NULLP(ZafIChar)))
				cp = _nullStr;
		if (prec >= 0)
		{
			// can't use strlen; can only look for the
			// NULL in the first `prec' characters, and
			// strlen() will go further.
			ZafIChar *p = cp;
			for (int _prec = prec; *p && _prec; p++, _prec--)
				;
			if (*p == '\0')
			{
				size = (int)(p - cp);
				if (size > prec)
					size = prec;
			}
			else
				size = prec;
		}
		else
			size = (int)strlen(cp);
		break;

	}
	int n;	// Scratch for PAD();
	/* right-adjusting blank padding */
	if ((flags & (ZAF_LADJUST | ZAF_ZEROPAD)) == 0)
		PAD(width - size, ' ');
	/* right-adjusting zero padding */
	if ((flags & (ZAF_LADJUST | ZAF_ZEROPAD)) == ZAF_ZEROPAD)
		PAD(width - size, '0');
	/* the string or number proper */
	PRINT(cp, size);
	/* left-adjusting padding (always blank) */
	if (flags & ZAF_LADJUST)
		PAD(width - size, ' ');
	*buffer = 0;
	*ptrBuffer = buffer;
}

#define LENGTHOF(x)	((int)(sizeof(x)/sizeof((x)[0])))

#if defined(ZAF_UNICODE)
	const int TABLE_SIZE = 8192;
#else
	const int TABLE_SIZE = 32;
#endif
// Fill in the given table from the scanset at the given format
// (just after `[').  Return a pointer to the character past the
// closing `]'.  The table has a 1 wherever characters should be
// considered part of the scanset.
static const ZafIChar *setLegal(char *table, const ZafIChar *fmt)
{
#if defined(ZAF_UNICODE)
	ZafIChar c, n;
#else
	unsigned char c, n;
#endif

	char v = 0;
	/* first `clear' the whole table */
#if defined(ZAF_UNICODE)
	c = (ZafIChar)(*fmt++);				/* first char hat => negated scanset */
#else
	c = (unsigned char)(*fmt++);		/* first char hat => negated scanset */
#endif
	if (c == '^')
	{
		v = (char)0xff;	/* default => accept */
#if defined(ZAF_UNICODE)
		c = (ZafIChar)(*fmt++);			/* get new first char */
#else
		c = (unsigned char)(*fmt++);	/* get new first char */
#endif
	}
	memset(table, v, TABLE_SIZE);

	if (c == 0)
		return (fmt - 1);/* format ended before closing ] */

	/*
	 * Now set the entries corresponding to the actual scanset
	 * to the opposite of the above.
	 *
	 * The first character may be ']' (or '-') without being special;
	 * the last character may be '-'.
	 */
	for (;;) {
		table[c >> 3] ^= (char)(1 << (c & 7)); // take character c
doswitch:
#if defined(ZAF_UNICODE)
		n = (ZafIChar)(*fmt++);			/* and examine the next */
#else
		n = (unsigned char)(*fmt++);	/* and examine the next */
#endif
		switch (n) {
		case 0:			/* format ended too soon */
			return (fmt - 1);
		case '-':
			/*
			 * A scanset of the form
			 *	[01+-]
			 * is defined as `the digit 0, the digit 1,
			 * the character +, the character -', but
			 * the effect of a scanset such as
			 *	[a-zA-Z0-9]
			 * is implementation defined.  The V7 Unix
			 * scanf treats `a-z' as `the letters a through
			 * z', but treats `a-a' as `the letter a, the
			 * character -, and the letter a'.
			 *
			 * For compatibility, the `-' is not considerd
			 * to define a range if the character following
			 * it is either a close bracket (required by ANSI)
			 * or is not numerically greater than the character
			 * we just stored in the table (c).
			 */
#if defined(ZAF_UNICODE)
			n = (ZafIChar)(*fmt);
#else
			n = (unsigned char)(*fmt);
#endif
			if (n == ']' || n < c)
			{
				c = '-';
				break;	/* resume the for(;;) */
			}
			fmt++;
			do {		/* fill in the range */
				c++;
				table[c >> 3] ^= (char)(1 << (c & 7)); // take character c
			} while (c < n);
#if 1	/* XXX another disgusting compatibility hack */
			/*
			 * Alas, the V7 Unix scanf also treats formats
			 * such as [a-c-e] as `the letters a through e'.
			 * This too is permitted by the standard....
			 */
			goto doswitch;
#else
			c = *fmt++;
			if (c == 0)
				return (fmt - 1);
			if (c == ']')
				return (fmt);
			break;
#endif
		case ']':		/* end of scanset */
			return (fmt);

		default:		/* just another character */
			c = n;
			break;
		}
	}
#if defined(_MSC_VER)
	return ZAF_NULLP(ZafIChar);
#endif
}

// This table is duplicated in u_ctype.cpp
static struct
{
	unsigned min;
	unsigned max;
} spaces[] =
{
	{ 0x0009, 0x000d },
	{ 0x0020, 0x0020 },
#if defined(ZAF_ISO8859_1)
	{ 0x00a0, 0x00a0 },
#elif defined(ZAF_UNICODE)
	{ 0x00a0, 0x00a0 },
	{ 0x2000, 0x200b },
	{ 0x2028, 0x2029 },
	{ 0x3000, 0x3000 },
	{ 0xfeff, 0xfeff },
#endif
};

int ZafStringData::Parse(va_list *argList, ZafIChar **ptrBuffer,
	ZafIChar fmtch, int flags, int width, const ZafIChar **fmt)
{
	ZafIChar *buffer = *ptrBuffer;
	if (fmtch == 'c' || fmtch == '[')
		flags |= ZAF_NOSKIP;
	if ((flags & ZAF_NOSKIP) == 0)
		while (isspace(*buffer))
			buffer++;

	char legal[TABLE_SIZE];	// character class table for %[...]

	switch (fmtch)
	{
	case 's':
		{
		// like CCL, but zero-length string OK, & no ZAF_NOSKIP
		if (width == 0)
			width = ~0;
		memset(legal, 0xff, TABLE_SIZE);
		// remove out the spaces
		for (int i = 0; i < LENGTHOF(spaces); i++)
			for (unsigned c = spaces[i].min; c <= spaces[i].max; c++)
				legal[c >> 3] &= (ZafIChar)~(char)(1 << (c & 7));
		}
		break;
	case 'c':
		// scan arbitrary characters (sets NOSKIP) 
		if (width == 0)
			width = 1;
		memset(legal, 0xff, TABLE_SIZE);
		break;
	case '[':
		if (width == 0)
			width = ~0;
		*fmt = setLegal(legal, *fmt);
		break;
	}
	int n = 0;
	if (flags & ZAF_SUPPRESS)
	{
		ZafIChar c = *buffer;
		while ((legal[c >> 3] & (1 << (c & 7))) && width && c)
		{
			buffer++;
			width--;
			c = *buffer;
			n++;
		}
	}
	else
	{
		ZafIChar *p;
		if (flags & ZAF_OBJ)
		{
			ZafStringData *tmp = va_arg(*argList, ZafStringData *);
			p = tmp->value;
		}
		else
			p = va_arg(*argList, ZafIChar *);
		ZafIChar c = *buffer;
		while ((legal[c >> 3] & (1 << (c & 7))) && width && c)
		{
			*p++ = c;
			buffer++;
			width--;
			c = *buffer;
			n++;
		}
		*p = 0;
	}
	*ptrBuffer = buffer;
	if (fmtch == '[' && n == 0)
		return -1;
	return 1;
}

#if defined(USE_ZAF_PERSISTENCE)
// ----- Persistent functions -----------------------------------------------
ZafStringData::ZafStringData(const ZafIChar *name, ZafDataPersistence &persist) :
	ZafFormatData(name, persist.PushLevel(className, classID, ZAF_PERSIST_FILE)),
	staticData(false), bufferSize(0), maxLength(-1), value(ZAF_NULLP(ZafIChar))
{
	// Read the object.
	if (persist.Error() == ZAF_ERROR_NONE)
		ZafStringData::ReadData(persist);

	persist.PopLevel();
	if (persist.Error() != ZAF_ERROR_NONE)
		SetError(persist.Error());
}

ZafElement *ZafStringData::Read(const ZafIChar *name, ZafDataPersistence &persist)
{
	return (new ZafStringData(name, persist));
}

void ZafStringData::ReadData(ZafDataPersistence &persist)
{
	// Read the data.
	ZafInt16 tMaxLength;
	ZafIChar *tString;
	ZafFile *file = persist.CurrentFile();
	*file >> tMaxLength >> &tString;
	if (tString)
	{
		SetText(tString, tMaxLength);
		delete []tString;
	}
	else
	{
		maxLength = tMaxLength;
		bufferSize = ZAF_STRING_DATA_LENGTH;
		value = new ZafIChar[bufferSize];
		strcpy(value, ZafLanguageManager::blankString);
	}
}

void ZafStringData::Write(ZafDataPersistence &persist)
{
	// Write the object.
	ZafFormatData::Write(persist.PushLevel(className, classID, ZAF_PERSIST_FILE));
	if (persist.Error() == ZAF_ERROR_NONE)
		ZafStringData::WriteData(persist);
	persist.PopLevel();
	if (persist.Error() != ZAF_ERROR_NONE)
		SetError(persist.Error());
}

void ZafStringData::WriteData(ZafDataPersistence &persist)
{
	// Write the data.
	ZafInt16 tMaxLength = (ZafInt16)maxLength;
	ZafFile *file = persist.CurrentFile();
	*file << tMaxLength << value;
}
#endif
