コード例 #1
0
ファイル: bhnd_nvram_subr.c プロジェクト: kwitaszczyk/freebsd
/**
 * Parse a 'name=value' string.
 * 
 * @param env The string to be parsed.
 * @param env_len The length of @p envp.
 * @param delim The delimiter used in @p envp. This will generally be '='.
 * @param[out] name If not NULL, a pointer to the name string. This argument
 * may be NULL.
 * @param[out] name_len On success, the length of the name substring. This
 * argument may be NULL.
 * @param[out] value On success, a pointer to the value substring. This argument
 * may be NULL.
 * @param[out] value_len On success, the length of the value substring. This
 * argument may be NULL.
 * 
 * @retval 0 success
 * @retval EINVAL if parsing @p envp fails.
 */
int
bhnd_nvram_parse_env(const char *env, size_t env_len, char delim,
    const char **name, size_t *name_len, const char **value, size_t *value_len)
{
	const char *p;

	/* Name */
	if ((p = memchr(env, delim, env_len)) == NULL) {
		BHND_NV_LOG("delimiter '%c' not found in '%.*s'\n", delim,
		    BHND_NV_PRINT_WIDTH(env_len), env);
		return (EINVAL);
	}

	/* Name */
	if (name != NULL)
		*name = env;
	if (name_len != NULL)
		*name_len = p - env;

	/* Skip delim */
	p++;

	/* Value */
	if (value != NULL)
		*value = p;
	if (value_len != NULL)
		*value_len = env_len - (p - env);

	return (0);
}
コード例 #2
0
ファイル: bhnd_nvram_subr.c プロジェクト: kwitaszczyk/freebsd
/**
 * Parses the string in the optionally NUL-terminated @p str to as an integer
 * value of @p otype, accepting any integer format supported by the standard
 * strtoul().
 * 
 * - Any leading whitespace in @p str -- as defined by the equivalent of
 *   calling isspace_l() with an ASCII locale -- will be ignored.
 * - A @p str may be prefixed with a single optional '+' or '-' sign denoting
 *   signedness.
 * - A hexadecimal @p str may include an '0x' or '0X' prefix, denoting that a
 *   base 16 integer follows.
 * - An octal @p str may include a '0' prefix, denoting that an octal integer
 *   follows.
 * 
 * If a @p base of 0 is specified, the base will be determined according
 * to the string's initial prefix, as per strtoul()'s documented behavior.
 *
 * When parsing a base 16 integer to a signed representation, if no explicit
 * sign prefix is given, the string will be parsed as the raw two's complement
 * representation of the signed integer value.
 *
 * @param		str	The string to be parsed.
 * @param		maxlen	The maximum number of bytes to be read in
 *				@p str.
 * @param		base	The input string's base (2-36), or 0.
 * @param[out]		nbytes	On success or failure, will be set to the total
 *				number of parsed bytes. If the total number of
 *				bytes is not desired, a NULL pointer may be
 *				provided.
 * @param[out]		outp	On success, the parsed integer value will be
 *				written to @p outp. This argment may be NULL if
 *				the value is not desired.
 * @param[in,out]	olen	The capacity of @p outp. On success, will be set
 *				to the actual size of the requested value.
 * @param		otype	The integer type to be parsed.
 *
 * @retval 0		success
 * @retval EINVAL	if an invalid @p base is specified.
 * @retval EINVAL	if an unsupported (or non-integer) @p otype is
 *			specified.
 * @retval ENOMEM	If @p outp is non-NULL and a buffer of @p olen is too
 *			small to hold the requested value.
 * @retval EFTYPE	if @p str cannot be parsed as an integer of @p base.
 * @retval ERANGE	If the integer parsed from @p str is too large to be
 *			represented as a value of @p otype.
 */
int
bhnd_nvram_parse_int(const char *str, size_t maxlen,  u_int base,
    size_t *nbytes, void *outp, size_t *olen, bhnd_nvram_type otype)
{
	uint64_t	value;
	uint64_t	carry_max, value_max;
	uint64_t	type_max;
	size_t		limit, local_nbytes;
	size_t		ndigits;
	bool		negative, sign, twos_compl;

	/* Must be an integer type */
	if (!bhnd_nvram_is_int_type(otype))
		return (EINVAL);

	/* Determine output byte limit */
	if (outp != NULL)
		limit = *olen;
	else
		limit = 0;

	/* We always need a byte count. If the caller provides a NULL nbytes,
	 * track our position in a stack variable */
	if (nbytes == NULL)
		nbytes = &local_nbytes;

	value = 0;
	ndigits = 0;
	*nbytes = 0;
	negative = false;
	sign = false;

	/* Validate the specified base */
	if (base != 0 && !(base >= 2 && base <= 36))
		return (EINVAL);

	/* Skip any leading whitespace */
	for (; *nbytes < maxlen; (*nbytes)++) {
		if (!bhnd_nv_isspace(str[*nbytes]))
			break;
	}

	/* Empty string? */
	if (*nbytes == maxlen)
		return (EFTYPE);

	/* Parse and skip sign */
	if (str[*nbytes] == '-') {
		negative = true;
		sign = true;
		(*nbytes)++;
	} else if (str[*nbytes] == '+') {
		sign = true;
		(*nbytes)++;
	}

	/* Truncated after sign character? */
	if (*nbytes == maxlen)
		return (EFTYPE);

	/* Identify (or validate) hex base, skipping 0x/0X prefix */
	if (base == 16 || base == 0) {
		/* Check for (and skip) 0x/0X prefix */
		if (maxlen - *nbytes >= 2 && str[*nbytes] == '0' &&
		    (str[*nbytes+1] == 'x' || str[*nbytes+1] == 'X'))
		{
			base = 16;
			(*nbytes) += 2;
		}
	}

	/* Truncated after hex prefix? */
	if (*nbytes == maxlen)
		return (EFTYPE);

	/* Differentiate decimal/octal by looking for a leading 0 */
	if (base == 0) {
		if (str[*nbytes] == '0') {
			base = 8;
		} else {
			base = 10;
		}
	}

	/* Only enable twos-compliment signed integer parsing enabled if the
	 * input is base 16, and no explicit sign prefix was provided */
	if (!sign && base == 16)
		twos_compl = true;
	else
		twos_compl = false;

	/* Determine the maximum value representable by the requested type */
	switch (otype) {
	case BHND_NVRAM_TYPE_CHAR:
	case BHND_NVRAM_TYPE_UINT8:
		type_max = (uint64_t)UINT8_MAX;
		break;
	case BHND_NVRAM_TYPE_UINT16:
		type_max = (uint64_t)UINT16_MAX;
		break;
	case BHND_NVRAM_TYPE_UINT32:
		type_max = (uint64_t)UINT32_MAX;
		break;
	case BHND_NVRAM_TYPE_UINT64:
		type_max = (uint64_t)UINT64_MAX;
		break;

	case BHND_NVRAM_TYPE_INT8:
		if (twos_compl)
			type_max = (uint64_t)UINT8_MAX;
		else if (negative)
			type_max = -(uint64_t)INT8_MIN;
		else
			type_max = (uint64_t)INT8_MAX;
		break;

	case BHND_NVRAM_TYPE_INT16:
		if (twos_compl)
			type_max = (uint64_t)UINT16_MAX;
		else if (negative)
			type_max = -(uint64_t)INT16_MIN;
		else
			type_max = (uint64_t)INT16_MAX;
		break;

	case BHND_NVRAM_TYPE_INT32:
		if (twos_compl)
			type_max = (uint64_t)UINT32_MAX;
		else if (negative)
			type_max = -(uint64_t)INT32_MIN;
		else
			type_max = (uint64_t)INT32_MAX;
		break;

	case BHND_NVRAM_TYPE_INT64:
		if (twos_compl)
			type_max = (uint64_t)UINT64_MAX;
		else if (negative)
			type_max = -(uint64_t)INT64_MIN;
		else
			type_max = (uint64_t)INT64_MAX;
		break;

	default:
		BHND_NV_LOG("unsupported integer type: %d\n", otype);
		return (EINVAL);
	}

	/* The maximum value after which an additional carry would overflow */
	value_max = type_max / (uint64_t)base;

	/* The maximum carry value given a value equal to value_max */
	carry_max = type_max % (uint64_t)base;

	/* Consume input until we hit maxlen or a non-digit character */
	for (; *nbytes < maxlen; (*nbytes)++) {
		u_long	carry;
		char	c;

		/* Parse carry value */
		c = str[*nbytes];
		if (bhnd_nv_isdigit(c)) {
			carry = c - '0';
		} else if (bhnd_nv_isxdigit(c)) {
			if (bhnd_nv_isupper(c))
				carry = (c - 'A') + 10;
			else
				carry = (c - 'a') + 10;
		} else {
			/* Hit first non-digit character */
			break;
		}

		/* If carry is outside the base, it's not a valid digit
		 * in the current parse context; consider it a non-digit
		 * character */
		if (carry >= (uint64_t)base)
			break;

		/* Increment count of parsed digits */
		ndigits++;

		if (value > value_max) {
			/* -Any- carry value would overflow */
			return (ERANGE);
		} else if (value == value_max && carry > carry_max) {
			/* -This- carry value would overflow */
			return (ERANGE);
		}

		value *= (uint64_t)base;
		value += carry;
	}

	/* If we hit a non-digit character before parsing the first digit,
	 * we hit an empty integer string. */
	if (ndigits == 0)
		return (EFTYPE);

	if (negative)
		value = -value;

	/* Provide (and verify) required length */
	*olen = bhnd_nvram_value_size(otype, NULL, 0, 1);
	if (outp == NULL)
		return (0);
	else if (limit < *olen)
		return (ENOMEM);

	/* Provide result */
	switch (otype) {
	case BHND_NVRAM_TYPE_CHAR:
	case BHND_NVRAM_TYPE_UINT8:
		*(uint8_t *)outp = (uint8_t)value;
		break;
	case BHND_NVRAM_TYPE_UINT16:
		*(uint16_t *)outp = (uint16_t)value;
		break;
	case BHND_NVRAM_TYPE_UINT32:
		*(uint32_t *)outp = (uint32_t)value;
		break;
	case BHND_NVRAM_TYPE_UINT64:
		*(uint64_t *)outp = (uint64_t)value;
		break;

	case BHND_NVRAM_TYPE_INT8:
		*(int8_t *)outp = (int8_t)(int64_t)value;
		break;
	case BHND_NVRAM_TYPE_INT16:
		*(int16_t *)outp = (int16_t)(int64_t)value;
		break;
	case BHND_NVRAM_TYPE_INT32:
		*(int32_t *)outp = (int32_t)(int64_t)value;
		break;
	case BHND_NVRAM_TYPE_INT64:
		*(int64_t *)outp = (int64_t)value;
		break;
	default:
		/* unreachable */
		BHND_NV_PANIC("unhandled type %d\n", otype);
	}

	return (0);
}
コード例 #3
0
ファイル: bhnd_nvram_subr.c プロジェクト: kwitaszczyk/freebsd
/**
 * Return the size, in bytes, of a value of @p type with @p nelem elements.
 * 
 * @param	type	The value type.
 * @param	data	The actual data to be queried, or NULL if unknown. If
 *			NULL and the base type is not a fixed width type
 *			(e.g. BHND_NVRAM_TYPE_STRING), 0 will be returned.
 * @param	nbytes	The size of @p data, in bytes, or 0 if @p data is NULL.
 * @param	nelem	The number of elements. If @p type is not an array type,
 *			this value must be 1.
 * 
 * @retval 0		If @p type has a variable width, and @p data is NULL.
 * @retval 0		If a @p nelem value greater than 1 is provided for a
 *			non-array @p type.
 * @retval 0		If a @p nelem value of 0 is provided.
 * @retval 0		If the result would exceed the maximum value
 *			representable by size_t.
 * @retval non-zero	The size, in bytes, of @p type with @p nelem elements.
 */
size_t
bhnd_nvram_value_size(bhnd_nvram_type type, const void *data, size_t nbytes,
    size_t nelem)
{
	/* If nelem 0, nothing to do */
	if (nelem == 0)
		return (0);

	/* Non-array types must have an nelem value of 1 */
	if (!bhnd_nvram_is_array_type(type) && nelem != 1)
		return (0);

	switch (type) {
	case BHND_NVRAM_TYPE_UINT8_ARRAY:
	case BHND_NVRAM_TYPE_UINT16_ARRAY:
	case BHND_NVRAM_TYPE_UINT32_ARRAY:
	case BHND_NVRAM_TYPE_UINT64_ARRAY:
	case BHND_NVRAM_TYPE_INT8_ARRAY:
	case BHND_NVRAM_TYPE_INT16_ARRAY:
	case BHND_NVRAM_TYPE_INT32_ARRAY:
	case BHND_NVRAM_TYPE_INT64_ARRAY:
	case BHND_NVRAM_TYPE_CHAR_ARRAY: {
		bhnd_nvram_type	base_type;
		size_t		base_size;

		base_type = bhnd_nvram_base_type(type);
		base_size = bhnd_nvram_value_size(base_type, NULL, 0, 1);

		/* Would nelem * base_size overflow? */
		if (SIZE_MAX / nelem < base_size) {
			BHND_NV_LOG("cannot represent size %s * %zu\n",
			    bhnd_nvram_type_name(base_type), nelem);
			return (0);
		}

		return (nelem * base_size);
	}

	case BHND_NVRAM_TYPE_STRING_ARRAY: {
		const char	*p;
		size_t		 total_size;

		if (data == NULL)
			return (0);

		/* Iterate over the NUL-terminated strings to calculate
		 * total byte length */
		p = data;
		total_size = 0;
		for (size_t i = 0; i < nelem; i++) {
			size_t	elem_size;

			elem_size = strnlen(p, nbytes - total_size);
			p += elem_size;

			/* Check for (and skip) terminating NUL */
			if (total_size < nbytes && *p == '\0') {
				elem_size++;
				p++;
			}

			/* Would total_size + elem_size overflow?
			 * 
			 * A memory range larger than SIZE_MAX shouldn't be,
			 * possible, but include the check for completeness */
			if (SIZE_MAX - total_size < elem_size)
				return (0);

			total_size += elem_size;
		}

		return (total_size);
	}

	case BHND_NVRAM_TYPE_STRING: {
		size_t size;

		if (data == NULL)
			return (0);

		/* Find length */
		size = strnlen(data, nbytes);

		/* Is there a terminating NUL, or did we just hit the
		 * end of the string input */
		if (size < nbytes)
			size++;

		return (size);
	}
	case BHND_NVRAM_TYPE_INT8:
	case BHND_NVRAM_TYPE_UINT8:
	case BHND_NVRAM_TYPE_CHAR:
		return (sizeof(uint8_t));

	case BHND_NVRAM_TYPE_INT16:
	case BHND_NVRAM_TYPE_UINT16:
		return (sizeof(uint16_t));

	case BHND_NVRAM_TYPE_INT32:
	case BHND_NVRAM_TYPE_UINT32:
		return (sizeof(uint32_t));

	case BHND_NVRAM_TYPE_UINT64:
	case BHND_NVRAM_TYPE_INT64:
		return (sizeof(uint64_t));
	}

	/* Quiesce gcc4.2 */
	BHND_NV_PANIC("bhnd nvram type %u unknown", type);
}
コード例 #4
0
/**
 * Format a string representation of the elements of @p value using @p fmt,
 * writing the result to @p outp.
 *
 * @param		value	The value to be formatted.
 * @param		fmt	The format string.
 * @param[out]		outp	On success, the string will be written to this 
 *				buffer. This argment may be NULL if the value is
 *				not desired.
 * @param[in,out]	olen	The capacity of @p outp. On success, will be set
 *				to the actual number of bytes required for the
 *				requested string encoding (including a trailing
 *				NUL).
 * @param		ap	Argument list.
 *
 * @par Format Strings
 * 
 * Value format strings are similar, but not identical to, those used
 * by printf(3).
 * 
 * Format specifier format:
 *     %[repeat][flags][width][.precision][length modifier][specifier]
 *
 * The format specifier is interpreted as an encoding directive for an
 * individual value element; each format specifier will fetch the next element
 * from the value, encode the element as the appropriate type based on the
 * length modifiers and specifier, and then format the result as a string.
 * 
 * For example, given a string value of '0x000F', and a format specifier of
 * '%#hhx', the value will be asked to encode its first element as
 * BHND_NVRAM_TYPE_UINT8. String formatting will then be applied to the 8-bit
 * unsigned integer representation, producing a string value of "0xF".
 * 
 * Repeat:
 * - [digits]		Repeatedly apply the format specifier to the input
 *			value's elements up to `digits` times. The delimiter
 *			must be passed as a string in the next variadic
 *			argument.
 * - []			Repeatedly apply the format specifier to the input
 *			value's elements until all elements have been. The
 *			processed. The delimiter must be passed as a string in
 *			the next variadic argument.
 * - [*]		Repeatedly apply the format specifier to the input
 *			value's elements. The repeat count is read from the
 *			next variadic argument as a size_t value
 * 
 * Flags:
 * - '#'		use alternative form (e.g. 0x/0X prefixing of hex
 *			strings).
 * - '0'		zero padding
 * - '-'		left adjust padding
 * - '+'		include a sign character
 * - ' '		include a space in place of a sign character for
 *			positive numbers.
 * 
 * Width/Precision:
 * - digits		minimum field width.
 * - *			read the minimum field width from the next variadic
 *			argument as a ssize_t value. A negative value enables
 *			left adjustment.
 * - .digits		field precision.
 * - .*			read the field precision from the next variadic argument
 *			as a ssize_t value. A negative value enables left
 *			adjustment.
 *
 * Length Modifiers:
 * - 'hh', 'I8'		Convert the value to an 8-bit signed or unsigned
 *			integer.
 * - 'h', 'I16'		Convert the value to an 16-bit signed or unsigned
 *			integer.
 * - 'l', 'I32'		Convert the value to an 32-bit signed or unsigned
 *			integer.
 * - 'll', 'j', 'I64'	Convert the value to an 64-bit signed or unsigned
 *			integer.
 * 
 * Data Specifiers:
 * - 'd', 'i'		Convert and format as a signed decimal integer.
 * - 'u'		Convert and format as an unsigned decimal integer.
 * - 'o'		Convert and format as an unsigned octal integer.
 * - 'x'		Convert and format as an unsigned hexadecimal integer,
 *			using lowercase hex digits.
 * - 'X'		Convert and format as an unsigned hexadecimal integer,
 *			using uppercase hex digits.
 * - 's'		Convert and format as a string.
 * - '%'		Print a literal '%' character.
 *
 * @retval 0		success
 * @retval EINVAL	If @p fmt contains unrecognized format string
 *			specifiers.
 * @retval ENOMEM	If the @p outp is non-NULL, and the provided @p olen
 *			is too small to hold the encoded value.
 * @retval EFTYPE	If value coercion from @p value to a single string
 *			value via @p fmt is unsupported.
 * @retval ERANGE	If value coercion of @p value would overflow (or
 *			underflow) the representation defined by @p fmt.
 */
int
bhnd_nvram_val_vprintf(bhnd_nvram_val *value, const char *fmt, char *outp,
    size_t *olen, va_list ap)
{
	const void	*elem;
	size_t		 elen;
	size_t		 limit, nbytes;
	int		 error;

	elem = NULL;

	/* Determine output byte limit */
	nbytes = 0;
	if (outp != NULL)
		limit = *olen;
	else
		limit = 0;

#define	WRITE_CHAR(_c)	do {			\
	if (limit > nbytes)			\
		*(outp + nbytes) = _c;		\
						\
	if (nbytes == SIZE_MAX)			\
		return (EFTYPE);		\
	nbytes++;				\
} while (0)

	/* Encode string value as per the format string */
	for (const char *p = fmt; *p != '\0'; p++) {
		const char	*delim;
		size_t		 precision, width, delim_len;
		u_long		 repeat, bits;
		bool		 alt_form, ladjust, have_precision;
		char		 padc, signc, lenc;

		padc = ' ';
		signc = '\0';
		lenc = '\0';
		delim = "";
		delim_len = 0;

		ladjust = false;
		alt_form = false;

		have_precision = false;
		precision = 1;
		bits = 32;
		width = 0;
		repeat = 1;

		/* Copy all input to output until we hit a format specifier */
		if (*p != '%') {
			WRITE_CHAR(*p);
			continue;
		}

		/* Hit '%' -- is this followed by an escaped '%' literal? */
		p++;
		if (*p == '%') {
			WRITE_CHAR('%');
			p++;
			continue;
		}

		/* Parse repeat specifier */
		if (*p == '[') {
			p++;
			
			/* Determine repeat count */
			if (*p == ']') {
				/* Repeat consumes all input */
				repeat = bhnd_nvram_val_nelem(value);
			} else if (*p == '*') {
				/* Repeat is supplied as an argument */
				repeat = va_arg(ap, size_t);
				p++;
			} else {
				char *endp;

				/* Repeat specified as argument */
				repeat = strtoul(p, &endp, 10);
				if (p == endp) {
					BHND_NV_LOG("error parsing repeat "
						    "count at '%s'", p);
					return (EINVAL);
				}
				
				/* Advance past repeat count */
				p = endp;
			}

			/* Advance past terminating ']' */
			if (*p != ']') {
				BHND_NV_LOG("error parsing repeat count at "
				    "'%s'", p);
				return (EINVAL);
			}
			p++;

			delim = va_arg(ap, const char *);
			delim_len = strlen(delim);
		}

		/* Parse flags */
		while (*p != '\0') {
			const char	*np;
			bool		 stop;

			stop = false;
			np = p+1;
	
			switch (*p) {
			case '#':
				alt_form = true;
				break;
			case '0':
				padc = '0';
				break;
			case '-':
				ladjust = true;
				break;
			case ' ':
				/* Must not override '+' */
				if (signc != '+')
					signc = ' ';
				break;
			case '+':
				signc = '+';
				break;
			default:
				/* Non-flag character */
				stop = true;
				break;
			}

			if (stop)
				break;
			else
				p = np;
		}

		/* Parse minimum width */
		if (*p == '*') {
			ssize_t arg;

			/* Width is supplied as an argument */
			arg = va_arg(ap, int);

			/* Negative width argument is interpreted as
			 * '-' flag followed by positive width */
			if (arg < 0) {
				ladjust = true;
				arg = -arg;
			}

			width = arg;
			p++;
		} else if (bhnd_nv_isdigit(*p)) {