TRIO_PRIVATE BOOLEAN_T TrioReadDouble TRIO_ARGS4((self, target, flags, width), trio_class_t* self,
                                                 trio_pointer_t target, trio_flags_t flags,
                                                 int width)
{
	int ch;
	char doubleString[512];
	int offset = 0;
	int start;
#if TRIO_FEATURE_QUOTE
	int j;
#endif
	BOOLEAN_T isHex = FALSE;
	trio_long_double_t infinity;

	doubleString[0] = 0;

	if ((width == NO_WIDTH) || (width > (int)sizeof(doubleString) - 1))
		width = sizeof(doubleString) - 1;

	TrioSkipWhitespaces(self);

	/*
	 * Read entire double number from stream. trio_to_double requires
	 * a string as input, but InStream can be anything, so we have to
	 * collect all characters.
	 */
	ch = self->current;
	if ((ch == '+') || (ch == '-'))
	{
		doubleString[offset++] = (char)ch;
		self->InStream(self, &ch);
		width--;
	}

	start = offset;
	switch (ch)
	{
		case 'n':
		case 'N':
			/* Not-a-number */
			if (offset != 0)
				break;
			/* FALLTHROUGH */
		case 'i':
		case 'I':
			/* Infinity */
			while (isalpha(ch) && (offset - start < width))
			{
				doubleString[offset++] = (char)ch;
				self->InStream(self, &ch);
			}
			doubleString[offset] = NIL;

			/* Case insensitive string comparison */
			if (trio_equal(&doubleString[start], INFINITE_UPPER) ||
			    trio_equal(&doubleString[start], LONG_INFINITE_UPPER))
			{
				infinity = ((start == 1) && (doubleString[0] == '-')) ? trio_ninf() : trio_pinf();
				if (!target)
					return FALSE;

				if (flags & FLAGS_LONGDOUBLE)
				{
					*((trio_long_double_t*)target) = infinity;
				}
				else if (flags & FLAGS_LONG)
				{
					*((double*)target) = infinity;
				}
				else
				{
					*((float*)target) = infinity;
				}
				return TRUE;
			}
			if (trio_equal(doubleString, NAN_UPPER))
			{
				if (!target)
					return FALSE;

				/* NaN must not have a preceeding + nor - */
				if (flags & FLAGS_LONGDOUBLE)
				{
					*((trio_long_double_t*)target) = trio_nan();
				}
				else if (flags & FLAGS_LONG)
				{
					*((double*)target) = trio_nan();
				}
				else
				{
					*((float*)target) = trio_nan();
				}
				return TRUE;
			}
			return FALSE;

		case '0':
			doubleString[offset++] = (char)ch;
			self->InStream(self, &ch);
			if (trio_to_upper(ch) == 'X')
			{
				isHex = TRUE;
				doubleString[offset++] = (char)ch;
				self->InStream(self, &ch);
			}
			break;

		default:
			break;
	}

	while ((ch != EOF) && (offset - start < width))
	{
		/* Integer part */
		if (isHex ? isxdigit(ch) : isdigit(ch))
		{
			doubleString[offset++] = (char)ch;
			self->InStream(self, &ch);
		}
#if TRIO_FEATURE_QUOTE
		else if (flags & FLAGS_QUOTE)
		{
			/* Compare with thousands separator */
			for (j = 0; internalThousandSeparator[j] && self->current; j++)
			{
				if (internalThousandSeparator[j] != self->current)
					break;

				self->InStream(self, &ch);
			}
			if (internalThousandSeparator[j])
				break; /* Mismatch */
			else
				continue; /* Match */
		}
#endif
		else
			break; /* while */
	}
	if (ch == '.')
	{
		/* Decimal part */
		doubleString[offset++] = (char)ch;
		self->InStream(self, &ch);
		while ((isHex ? isxdigit(ch) : isdigit(ch)) && (offset - start < width))
		{
			doubleString[offset++] = (char)ch;
			self->InStream(self, &ch);
		}
	}
	if (isHex ? (trio_to_upper(ch) == 'P') : (trio_to_upper(ch) == 'E'))
	{
		/* Exponent */
		doubleString[offset++] = (char)ch;
		self->InStream(self, &ch);
		if ((ch == '+') || (ch == '-'))
		{
			doubleString[offset++] = (char)ch;
			self->InStream(self, &ch);
		}
		while (isdigit(ch) && (offset - start < width))
		{
			doubleString[offset++] = (char)ch;
			self->InStream(self, &ch);
		}
	}

	if ((offset == start) || (*doubleString == NIL))
		return FALSE;

	doubleString[offset] = 0;

	if (flags & FLAGS_LONGDOUBLE)
	{
		if (!target)
			return FALSE;

		*((trio_long_double_t*)target) = trio_to_long_double(doubleString, NULL);
	}
	else if (flags & FLAGS_LONG)
	{
		if (!target)
			return FALSE;

		*((double*)target) = trio_to_double(doubleString, NULL);
	}
	else
	{
		if (!target)
			return FALSE;

		*((float*)target) = trio_to_float(doubleString, NULL);
	}
	return TRUE;
}