TRIO_PRIVATE int TrioParse TRIO_ARGS6((type, format, parameters, arglist, argfunc, argarray),
                                      int type, TRIO_CONST char* format,
                                      trio_parameter_t* parameters, va_list arglist,
                                      trio_argfunc_t argfunc, trio_pointer_t* argarray)
{
	/* Count the number of times a parameter is referenced */
	unsigned short usedEntries[MAX_PARAMETERS];
	/* Parameter counters */
	int parameterPosition;
	int maxParam = -1;
	/* Utility variables */
	int offset;           /* Offset into formatting string */
	BOOLEAN_T positional; /* Does the specifier have a positional? */
#if TRIO_FEATURE_STICKY
	BOOLEAN_T gotSticky = FALSE; /* Are there any sticky modifiers at all? */
#endif
	/*
	 * indices specifies the order in which the parameters must be
	 * read from the va_args (this is necessary to handle positionals)
	 */
	int indices[MAX_PARAMETERS];
	int pos = 0;
	/* Various variables */
#if defined(TRIO_COMPILER_SUPPORTS_MULTIBYTE)
	int charlen;
#endif
	int save_errno;
	int i = -1;
	int num;
	trio_parameter_t workParameter;
	int status;

	/* Both must be set or none must be set */
	assert(((argfunc == NULL) && (argarray == NULL)) || ((argfunc != NULL) && (argarray != NULL)));

	/*
	 * The 'parameters' array is not initialized, but we need to
	 * know which entries we have used.
	 */
	memset(usedEntries, 0, sizeof(usedEntries));

	save_errno = errno;
	offset = 0;
	parameterPosition = 0;
#if defined(TRIO_COMPILER_SUPPORTS_MULTIBYTE)
	(void)mblen(NULL, 0);
#endif

	while (format[offset])
	{
		TrioInitializeParameter(&workParameter);

#if defined(TRIO_COMPILER_SUPPORTS_MULTIBYTE)
		if (!isascii(format[offset]))
		{
			/*
			 * Multibyte characters cannot be legal specifiers or
			 * modifiers, so we skip over them.
			 */
			charlen = mblen(&format[offset], MB_LEN_MAX);
			offset += (charlen > 0) ? charlen : 1;
			continue; /* while */
		}
#endif /* TRIO_COMPILER_SUPPORTS_MULTIBYTE */

		switch (format[offset++])
		{

			case CHAR_IDENTIFIER:
			{
				if (CHAR_IDENTIFIER == format[offset])
				{
					/* skip double "%" */
					offset++;
					continue; /* while */
				}

				status = TrioParseQualifiers(type, format, offset, &workParameter);
				if (status < 0)
					return status; /* Return qualifier syntax error */

				status = TrioParseSpecifier(type, format, workParameter.endOffset, &workParameter);
				if (status < 0)
					return status; /* Return specifier syntax error */
			}
			break;

#if TRIO_EXTENSION
			case CHAR_ALT_IDENTIFIER:
			{
				status = TrioParseQualifiers(type, format, offset, &workParameter);
				if (status < 0)
					continue; /* False alert, not a user defined specifier */

				status = TrioParseSpecifier(type, format, workParameter.endOffset, &workParameter);
				if ((status < 0) || (FORMAT_USER_DEFINED != workParameter.type))
					continue; /* False alert, not a user defined specifier */
			}
			break;
#endif

			default:
				continue; /* while */
		}

		/* now handle the parsed conversion specification */
		positional = (NO_POSITION != workParameter.position);

		/*
		 * Parameters only need the type and value. The value is
		 * read later.
		 */
		if (workParameter.flags & FLAGS_WIDTH_PARAMETER)
		{
			if (workParameter.width == NO_WIDTH)
			{
				workParameter.width = parameterPosition++;
			}
			else
			{
				if (!positional)
					workParameter.position = workParameter.width + 1;
			}

			usedEntries[workParameter.width] += 1;
			if (workParameter.width > maxParam)
				maxParam = workParameter.width;
			parameters[pos].type = FORMAT_PARAMETER;
			parameters[pos].flags = 0;
			indices[workParameter.width] = pos;
			workParameter.width = pos++;
		}
		if (workParameter.flags & FLAGS_PRECISION_PARAMETER)
		{
			if (workParameter.precision == NO_PRECISION)
			{
				workParameter.precision = parameterPosition++;
			}
			else
			{
				if (!positional)
					workParameter.position = workParameter.precision + 1;
			}

			usedEntries[workParameter.precision] += 1;
			if (workParameter.precision > maxParam)
				maxParam = workParameter.precision;
			parameters[pos].type = FORMAT_PARAMETER;
			parameters[pos].flags = 0;
			indices[workParameter.precision] = pos;
			workParameter.precision = pos++;
		}
		if (workParameter.flags & FLAGS_BASE_PARAMETER)
		{
			if (workParameter.base == NO_BASE)
			{
				workParameter.base = parameterPosition++;
			}
			else
			{
				if (!positional)
					workParameter.position = workParameter.base + 1;
			}

			usedEntries[workParameter.base] += 1;
			if (workParameter.base > maxParam)
				maxParam = workParameter.base;
			parameters[pos].type = FORMAT_PARAMETER;
			parameters[pos].flags = 0;
			indices[workParameter.base] = pos;
			workParameter.base = pos++;
		}
#if TRIO_FEATURE_VARSIZE
		if (workParameter.flags & FLAGS_VARSIZE_PARAMETER)
		{
			workParameter.varsize = parameterPosition++;

			usedEntries[workParameter.varsize] += 1;
			if (workParameter.varsize > maxParam)
				maxParam = workParameter.varsize;
			parameters[pos].type = FORMAT_PARAMETER;
			parameters[pos].flags = 0;
			indices[workParameter.varsize] = pos;
			workParameter.varsize = pos++;
		}
#endif
#if TRIO_FEATURE_USER_DEFINED
		if (workParameter.flags & FLAGS_USER_DEFINED_PARAMETER)
		{
			workParameter.user_defined.handler = parameterPosition++;

			usedEntries[workParameter.user_defined.handler] += 1;
			if (workParameter.user_defined.handler > maxParam)
				maxParam = workParameter.user_defined.handler;
			parameters[pos].type = FORMAT_PARAMETER;
			parameters[pos].flags = FLAGS_USER_DEFINED;
			indices[workParameter.user_defined.handler] = pos;
			workParameter.user_defined.handler = pos++;
		}
#endif

		if (NO_POSITION == workParameter.position)
		{
			workParameter.position = parameterPosition++;
		}

		if (workParameter.position > maxParam)
			maxParam = workParameter.position;

		if (workParameter.position >= MAX_PARAMETERS)
		{
			/* Bail out completely to make the error more obvious */
			return TRIO_ERROR_RETURN(TRIO_ETOOMANY, offset);
		}

		indices[workParameter.position] = pos;

		/*  Count the number of times this entry has been used */
		usedEntries[workParameter.position] += 1;

		/* Find last sticky parameters */
#if TRIO_FEATURE_STICKY
		if (workParameter.flags & FLAGS_STICKY)
		{
			gotSticky = TRUE;
		}
		else if (gotSticky)
		{
			for (i = pos - 1; i >= 0; i--)
			{
				if (parameters[i].type == FORMAT_PARAMETER)
					continue;
				if ((parameters[i].flags & FLAGS_STICKY) &&
				    (parameters[i].type == workParameter.type))
				{
					/* Do not overwrite current qualifiers */
					workParameter.flags |= (parameters[i].flags & (unsigned long)~FLAGS_STICKY);
					if (workParameter.width == NO_WIDTH)
						workParameter.width = parameters[i].width;
					if (workParameter.precision == NO_PRECISION)
						workParameter.precision = parameters[i].precision;
					if (workParameter.base == NO_BASE)
						workParameter.base = parameters[i].base;
					break;
				}
			}
		}
#endif

		if (workParameter.base == NO_BASE)
			workParameter.base = BASE_DECIMAL;

		offset = workParameter.endOffset;

		TrioCopyParameter(&parameters[pos++], &workParameter);
	} /* while format characters left */

	parameters[pos].type = FORMAT_SENTINEL; /* end parameter array with sentinel */
	parameters[pos].beginOffset = offset;

	for (num = 0; num <= maxParam; num++)
	{
		if (usedEntries[num] != 1)
		{
			if (usedEntries[num] == 0) /* gap detected */
				return TRIO_ERROR_RETURN(TRIO_EGAP, num);
			else /* double references detected */
				return TRIO_ERROR_RETURN(TRIO_EDBLREF, num);
		}

		i = indices[num];

		/*
		 * FORMAT_PARAMETERS are only present if they must be read,
		 * so it makes no sense to check the ignore flag (besides,
		 * the flags variable is not set for that particular type)
		 */
		if ((parameters[i].type != FORMAT_PARAMETER) && (parameters[i].flags & FLAGS_IGNORE))
			continue; /* for all arguments */

		/*
		 * The stack arguments are read according to ANSI C89
		 * default argument promotions:
		 *
		 *  char           = int
		 *  short          = int
		 *  unsigned char  = unsigned int
		 *  unsigned short = unsigned int
		 *  float          = double
		 *
		 * In addition to the ANSI C89 these types are read (the
		 * default argument promotions of C99 has not been
		 * considered yet)
		 *
		 *  long long
		 *  long double
		 *  size_t
		 *  ptrdiff_t
		 *  intmax_t
		 */
		switch (parameters[i].type)
		{
			case FORMAT_GROUP:
			case FORMAT_STRING:
#if TRIO_FEATURE_WIDECHAR
				if (parameters[i].flags & FLAGS_WIDECHAR)
				{
					parameters[i].data.wstring =
					    (argfunc == NULL)
					        ? va_arg(arglist, trio_wchar_t*)
					        : (trio_wchar_t*)(argfunc(argarray, num, TRIO_TYPE_PWCHAR));
				}
				else
#endif
				{
					parameters[i].data.string =
					    (argfunc == NULL) ? va_arg(arglist, char*)
					                      : (char*)(argfunc(argarray, num, TRIO_TYPE_PCHAR));
				}
				break;

#if TRIO_FEATURE_USER_DEFINED
			case FORMAT_USER_DEFINED:
#endif
			case FORMAT_POINTER:
			case FORMAT_COUNT:
			case FORMAT_UNKNOWN:
				parameters[i].data.pointer = (argfunc == NULL)
				                                 ? va_arg(arglist, trio_pointer_t)
				                                 : argfunc(argarray, num, TRIO_TYPE_POINTER);
				break;

			case FORMAT_CHAR:
			case FORMAT_INT:
#if TRIO_FEATURE_SCANF
				if (TYPE_SCAN == type)
				{
					if (argfunc == NULL)
						parameters[i].data.pointer =
						    (trio_pointer_t)va_arg(arglist, trio_pointer_t);
					else
					{
						if (parameters[i].type == FORMAT_CHAR)
							parameters[i].data.pointer =
							    (trio_pointer_t)((char*)argfunc(argarray, num, TRIO_TYPE_CHAR));
						else if (parameters[i].flags & FLAGS_SHORT)
							parameters[i].data.pointer =
							    (trio_pointer_t)((short*)argfunc(argarray, num, TRIO_TYPE_SHORT));
						else
							parameters[i].data.pointer =
							    (trio_pointer_t)((int*)argfunc(argarray, num, TRIO_TYPE_INT));
					}
				}
				else
#endif /* TRIO_FEATURE_SCANF */
				{
#if TRIO_FEATURE_VARSIZE || TRIO_FEATURE_FIXED_SIZE
					if (parameters[i].flags & (FLAGS_VARSIZE_PARAMETER | FLAGS_FIXED_SIZE))
					{
						int varsize;
						if (parameters[i].flags & FLAGS_VARSIZE_PARAMETER)
						{
							/*
							 * Variable sizes are mapped onto the fixed sizes, in
							 * accordance with integer promotion.
							 *
							 * Please note that this may not be portable, as we
							 * only guess the size, not the layout of the numbers.
							 * For example, if int is little-endian, and long is
							 * big-endian, then this will fail.
							 */
							varsize =
							    (int)parameters[parameters[i].varsize].data.number.as_unsigned;
						}
						else
						{
							/* Used for the I<bits> modifiers */
							varsize = parameters[i].varsize;
						}
						parameters[i].flags &= ~FLAGS_ALL_VARSIZES;

						if (varsize <= (int)sizeof(int))
							;
						else if (varsize <= (int)sizeof(long))
							parameters[i].flags |= FLAGS_LONG;
#if TRIO_FEATURE_INTMAX_T
						else if (varsize <= (int)sizeof(trio_longlong_t))
							parameters[i].flags |= FLAGS_QUAD;
						else
							parameters[i].flags |= FLAGS_INTMAX_T;
#else
						else
							parameters[i].flags |= FLAGS_QUAD;
#endif
					}
#endif /* TRIO_FEATURE_VARSIZE */
#if TRIO_FEATURE_SIZE_T || TRIO_FEATURE_SIZE_T_UPPER
					if (parameters[i].flags & FLAGS_SIZE_T)
						parameters[i].data.number.as_unsigned =
						    (argfunc == NULL)
						        ? (trio_uintmax_t)va_arg(arglist, size_t)
						        : (trio_uintmax_t)(
						              *((size_t*)argfunc(argarray, num, TRIO_TYPE_SIZE)));
					else
#endif
#if TRIO_FEATURE_PTRDIFF_T
					    if (parameters[i].flags & FLAGS_PTRDIFF_T)
						parameters[i].data.number.as_unsigned =
						    (argfunc == NULL)
						        ? (trio_uintmax_t)va_arg(arglist, ptrdiff_t)
						        : (trio_uintmax_t)(
						              *((ptrdiff_t*)argfunc(argarray, num, TRIO_TYPE_PTRDIFF)));
					else
#endif
#if TRIO_FEATURE_INTMAX_T
					    if (parameters[i].flags & FLAGS_INTMAX_T)
						parameters[i].data.number.as_unsigned =
						    (argfunc == NULL)
						        ? (trio_uintmax_t)va_arg(arglist, trio_intmax_t)
						        : (trio_uintmax_t)(
						              *((trio_intmax_t*)argfunc(argarray, num, TRIO_TYPE_UINTMAX)));
					else
#endif
					    if (parameters[i].flags & FLAGS_QUAD)
						parameters[i].data.number.as_unsigned =
						    (argfunc == NULL) ? (trio_uintmax_t)va_arg(arglist, trio_ulonglong_t)
						                      : (trio_uintmax_t)(*((trio_ulonglong_t*)argfunc(
						                            argarray, num, TRIO_TYPE_ULONGLONG)));
					else if (parameters[i].flags & FLAGS_LONG)
						parameters[i].data.number.as_unsigned =
						    (argfunc == NULL) ? (trio_uintmax_t)va_arg(arglist, long)
						                      : (trio_uintmax_t)(*(
						                            (long*)argfunc(argarray, num, TRIO_TYPE_LONG)));
					else
					{
						if (argfunc == NULL)
							parameters[i].data.number.as_unsigned =
							    (trio_uintmax_t)va_arg(arglist, int);
						else
						{
							if (parameters[i].type == FORMAT_CHAR)
								parameters[i].data.number.as_unsigned = (trio_uintmax_t)(
								    *((char*)argfunc(argarray, num, TRIO_TYPE_CHAR)));
							else if (parameters[i].flags & FLAGS_SHORT)
								parameters[i].data.number.as_unsigned = (trio_uintmax_t)(
								    *((short*)argfunc(argarray, num, TRIO_TYPE_SHORT)));
							else
								parameters[i].data.number.as_unsigned = (trio_uintmax_t)(
								    *((int*)argfunc(argarray, num, TRIO_TYPE_INT)));
						}
					}
				}
				break;

			case FORMAT_PARAMETER:
				/*
				 * The parameter for the user-defined specifier is a pointer,
				 * whereas the rest (width, precision, base) uses an integer.
				 */
				if (parameters[i].flags & FLAGS_USER_DEFINED)
					parameters[i].data.pointer = (argfunc == NULL)
					                                 ? va_arg(arglist, trio_pointer_t)
					                                 : argfunc(argarray, num, TRIO_TYPE_POINTER);
				else
					parameters[i].data.number.as_unsigned =
					    (argfunc == NULL)
					        ? (trio_uintmax_t)va_arg(arglist, int)
					        : (trio_uintmax_t)(*((int*)argfunc(argarray, num, TRIO_TYPE_INT)));
				break;

#if TRIO_FEATURE_FLOAT
			case FORMAT_DOUBLE:
#if TRIO_FEATURE_SCANF
				if (TYPE_SCAN == type)
				{
					if (parameters[i].flags & FLAGS_LONGDOUBLE)
						parameters[i].data.longdoublePointer =
						    (argfunc == NULL)
						        ? va_arg(arglist, trio_long_double_t*)
						        : (trio_long_double_t*)argfunc(argarray, num, TRIO_TYPE_LONGDOUBLE);
					else
					{
						if (parameters[i].flags & FLAGS_LONG)
							parameters[i].data.doublePointer =
							    (argfunc == NULL)
							        ? va_arg(arglist, double*)
							        : (double*)argfunc(argarray, num, TRIO_TYPE_DOUBLE);
						else
							parameters[i].data.doublePointer =
							    (argfunc == NULL)
							        ? (double*)va_arg(arglist, float*)
							        : (double*)argfunc(argarray, num, TRIO_TYPE_DOUBLE);
					}
				}
				else
#endif /* TRIO_FEATURE_SCANF */
				{
					if (parameters[i].flags & FLAGS_LONGDOUBLE)
						parameters[i].data.longdoubleNumber =
						    (argfunc == NULL) ? va_arg(arglist, trio_long_double_t)
						                      : (trio_long_double_t)(*((trio_long_double_t*)argfunc(
						                            argarray, num, TRIO_TYPE_LONGDOUBLE)));
					else
					{
						if (argfunc == NULL)
							parameters[i].data.longdoubleNumber =
							    (trio_long_double_t)va_arg(arglist, double);
						else
						{
							if (parameters[i].flags & FLAGS_SHORT)
								parameters[i].data.longdoubleNumber = (trio_long_double_t)(
								    *((float*)argfunc(argarray, num, TRIO_TYPE_FLOAT)));
							else
								parameters[i].data.longdoubleNumber = (trio_long_double_t)(
								    *((double*)argfunc(argarray, num, TRIO_TYPE_DOUBLE)));
						}
					}
				}
				break;
#endif /* TRIO_FEATURE_FLOAT */

#if TRIO_FEATURE_ERRNO
			case FORMAT_ERRNO:
				parameters[i].data.errorNumber = save_errno;
				break;
#endif

			default:
				break;
		}
	} /* for all specifiers */
	return num;
}