516 lines · 10.0 KB · C
RawDownload
/* * Helpers for formatting and printing strings * * Copyright 31 August 2008 James Bottomley * Copyright (C) 2013, Intel Corporation */#include <linux/kernel.h>#include <linux/math64.h>#include <linux/export.h>#include <linux/ctype.h>#include <linux/errno.h>#include <linux/string.h>#include <linux/string_helpers.h> /** * string_get_size - get the size in the specified units * @size:	The size to be converted * @units:	units to use (powers of 1000 or 1024) * @buf:	buffer to format to * @len:	length of buffer * * This function returns a string formatted to 3 significant figures * giving the size in the required units.  Returns 0 on success or * error on failure.  @buf is always zero terminated. * */int string_get_size(u64 size, const enum string_size_units units,		    char *buf, int len){	static const char *const units_10[] = {		"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB", NULL	};	static const char *const units_2[] = {		"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB",		NULL	};	static const char *const *const units_str[] = {		[STRING_UNITS_10] = units_10,		[STRING_UNITS_2] = units_2,	};	static const unsigned int divisor[] = {		[STRING_UNITS_10] = 1000,		[STRING_UNITS_2] = 1024,	};	int i, j;	u64 remainder = 0, sf_cap;	char tmp[8]; 	tmp[0] = '\0';	i = 0;	if (size >= divisor[units]) {		while (size >= divisor[units] && units_str[units][i]) {			remainder = do_div(size, divisor[units]);			i++;		} 		sf_cap = size;		for (j = 0; sf_cap*10 < 1000; j++)			sf_cap *= 10; 		if (j) {			remainder *= 1000;			do_div(remainder, divisor[units]);			snprintf(tmp, sizeof(tmp), ".%03lld",				 (unsigned long long)remainder);			tmp[j+1] = '\0';		}	} 	snprintf(buf, len, "%lld%s %s", (unsigned long long)size,		 tmp, units_str[units][i]); 	return 0;}EXPORT_SYMBOL(string_get_size); static bool unescape_space(char **src, char **dst){	char *p = *dst, *q = *src; 	switch (*q) {	case 'n':		*p = '\n';		break;	case 'r':		*p = '\r';		break;	case 't':		*p = '\t';		break;	case 'v':		*p = '\v';		break;	case 'f':		*p = '\f';		break;	default:		return false;	}	*dst += 1;	*src += 1;	return true;} static bool unescape_octal(char **src, char **dst){	char *p = *dst, *q = *src;	u8 num; 	if (isodigit(*q) == 0)		return false; 	num = (*q++) & 7;	while (num < 32 && isodigit(*q) && (q - *src < 3)) {		num <<= 3;		num += (*q++) & 7;	}	*p = num;	*dst += 1;	*src = q;	return true;} static bool unescape_hex(char **src, char **dst){	char *p = *dst, *q = *src;	int digit;	u8 num; 	if (*q++ != 'x')		return false; 	num = digit = hex_to_bin(*q++);	if (digit < 0)		return false; 	digit = hex_to_bin(*q);	if (digit >= 0) {		q++;		num = (num << 4) | digit;	}	*p = num;	*dst += 1;	*src = q;	return true;} static bool unescape_special(char **src, char **dst){	char *p = *dst, *q = *src; 	switch (*q) {	case '\"':		*p = '\"';		break;	case '\\':		*p = '\\';		break;	case 'a':		*p = '\a';		break;	case 'e':		*p = '\e';		break;	default:		return false;	}	*dst += 1;	*src += 1;	return true;} /** * string_unescape - unquote characters in the given string * @src:	source buffer (escaped) * @dst:	destination buffer (unescaped) * @size:	size of the destination buffer (0 to unlimit) * @flags:	combination of the flags (bitwise OR): *	%UNESCAPE_SPACE: *		'\f' - form feed *		'\n' - new line *		'\r' - carriage return *		'\t' - horizontal tab *		'\v' - vertical tab *	%UNESCAPE_OCTAL: *		'\NNN' - byte with octal value NNN (1 to 3 digits) *	%UNESCAPE_HEX: *		'\xHH' - byte with hexadecimal value HH (1 to 2 digits) *	%UNESCAPE_SPECIAL: *		'\"' - double quote *		'\\' - backslash *		'\a' - alert (BEL) *		'\e' - escape *	%UNESCAPE_ANY: *		all previous together * * Description: * The function unquotes characters in the given string. * * Because the size of the output will be the same as or less than the size of * the input, the transformation may be performed in place. * * Caller must provide valid source and destination pointers. Be aware that * destination buffer will always be NULL-terminated. Source string must be * NULL-terminated as well. * * Return: * The amount of the characters processed to the destination buffer excluding * trailing '\0' is returned. */int string_unescape(char *src, char *dst, size_t size, unsigned int flags){	char *out = dst; 	while (*src && --size) {		if (src[0] == '\\' && src[1] != '\0' && size > 1) {			src++;			size--; 			if (flags & UNESCAPE_SPACE &&					unescape_space(&src, &out))				continue; 			if (flags & UNESCAPE_OCTAL &&					unescape_octal(&src, &out))				continue; 			if (flags & UNESCAPE_HEX &&					unescape_hex(&src, &out))				continue; 			if (flags & UNESCAPE_SPECIAL &&					unescape_special(&src, &out))				continue; 			*out++ = '\\';		}		*out++ = *src++;	}	*out = '\0'; 	return out - dst;}EXPORT_SYMBOL(string_unescape); static int escape_passthrough(unsigned char c, char **dst, size_t *osz){	char *out = *dst; 	if (*osz < 1)		return -ENOMEM; 	*out++ = c; 	*dst = out;	*osz -= 1; 	return 1;} static int escape_space(unsigned char c, char **dst, size_t *osz){	char *out = *dst;	unsigned char to; 	if (*osz < 2)		return -ENOMEM; 	switch (c) {	case '\n':		to = 'n';		break;	case '\r':		to = 'r';		break;	case '\t':		to = 't';		break;	case '\v':		to = 'v';		break;	case '\f':		to = 'f';		break;	default:		return 0;	} 	*out++ = '\\';	*out++ = to; 	*dst = out;	*osz -= 2; 	return 1;} static int escape_special(unsigned char c, char **dst, size_t *osz){	char *out = *dst;	unsigned char to; 	if (*osz < 2)		return -ENOMEM; 	switch (c) {	case '\\':		to = '\\';		break;	case '\a':		to = 'a';		break;	case '\e':		to = 'e';		break;	default:		return 0;	} 	*out++ = '\\';	*out++ = to; 	*dst = out;	*osz -= 2; 	return 1;} static int escape_null(unsigned char c, char **dst, size_t *osz){	char *out = *dst; 	if (*osz < 2)		return -ENOMEM; 	if (c)		return 0; 	*out++ = '\\';	*out++ = '0'; 	*dst = out;	*osz -= 2; 	return 1;} static int escape_octal(unsigned char c, char **dst, size_t *osz){	char *out = *dst; 	if (*osz < 4)		return -ENOMEM; 	*out++ = '\\';	*out++ = ((c >> 6) & 0x07) + '0';	*out++ = ((c >> 3) & 0x07) + '0';	*out++ = ((c >> 0) & 0x07) + '0'; 	*dst = out;	*osz -= 4; 	return 1;} static int escape_hex(unsigned char c, char **dst, size_t *osz){	char *out = *dst; 	if (*osz < 4)		return -ENOMEM; 	*out++ = '\\';	*out++ = 'x';	*out++ = hex_asc_hi(c);	*out++ = hex_asc_lo(c); 	*dst = out;	*osz -= 4; 	return 1;} /** * string_escape_mem - quote characters in the given memory buffer * @src:	source buffer (unescaped) * @isz:	source buffer size * @dst:	destination buffer (escaped) * @osz:	destination buffer size * @flags:	combination of the flags (bitwise OR): *	%ESCAPE_SPACE: *		'\f' - form feed *		'\n' - new line *		'\r' - carriage return *		'\t' - horizontal tab *		'\v' - vertical tab *	%ESCAPE_SPECIAL: *		'\\' - backslash *		'\a' - alert (BEL) *		'\e' - escape *	%ESCAPE_NULL: *		'\0' - null *	%ESCAPE_OCTAL: *		'\NNN' - byte with octal value NNN (3 digits) *	%ESCAPE_ANY: *		all previous together *	%ESCAPE_NP: *		escape only non-printable characters (checked by isprint) *	%ESCAPE_ANY_NP: *		all previous together *	%ESCAPE_HEX: *		'\xHH' - byte with hexadecimal value HH (2 digits) * @esc:	NULL-terminated string of characters any of which, if found in *		the source, has to be escaped * * Description: * The process of escaping byte buffer includes several parts. They are applied * in the following sequence. *	1. The character is matched to the printable class, if asked, and in *	   case of match it passes through to the output. *	2. The character is not matched to the one from @esc string and thus *	   must go as is to the output. *	3. The character is checked if it falls into the class given by @flags. *	   %ESCAPE_OCTAL and %ESCAPE_HEX are going last since they cover any *	   character. Note that they actually can't go together, otherwise *	   %ESCAPE_HEX will be ignored. * * Caller must provide valid source and destination pointers. Be aware that * destination buffer will not be NULL-terminated, thus caller have to append * it if needs. * * Return: * The amount of the characters processed to the destination buffer, or * %-ENOMEM if the size of buffer is not enough to put an escaped character is * returned. * * Even in the case of error @dst pointer will be updated to point to the byte * after the last processed character. */int string_escape_mem(const char *src, size_t isz, char **dst, size_t osz,		      unsigned int flags, const char *esc){	char *out = *dst, *p = out;	bool is_dict = esc && *esc;	int ret = 0; 	while (isz--) {		unsigned char c = *src++; 		/*		 * Apply rules in the following sequence:		 *	- the character is printable, when @flags has		 *	  %ESCAPE_NP bit set		 *	- the @esc string is supplied and does not contain a		 *	  character under question		 *	- the character doesn't fall into a class of symbols		 *	  defined by given @flags		 * In these cases we just pass through a character to the		 * output buffer.		 */		if ((flags & ESCAPE_NP && isprint(c)) ||		    (is_dict && !strchr(esc, c))) {			/* do nothing */		} else {			if (flags & ESCAPE_SPACE) {				ret = escape_space(c, &p, &osz);				if (ret < 0)					break;				if (ret > 0)					continue;			} 			if (flags & ESCAPE_SPECIAL) {				ret = escape_special(c, &p, &osz);				if (ret < 0)					break;				if (ret > 0)					continue;			} 			if (flags & ESCAPE_NULL) {				ret = escape_null(c, &p, &osz);				if (ret < 0)					break;				if (ret > 0)					continue;			} 			/* ESCAPE_OCTAL and ESCAPE_HEX always go last */			if (flags & ESCAPE_OCTAL) {				ret = escape_octal(c, &p, &osz);				if (ret < 0)					break;				continue;			}			if (flags & ESCAPE_HEX) {				ret = escape_hex(c, &p, &osz);				if (ret < 0)					break;				continue;

Lines 1–500 of 516.