xref: /linux/lib/string_helpers.c (revision c8250381c8272a9828fdd353171727b154fbd296)
13c9f3681SJames Bottomley /*
23c9f3681SJames Bottomley  * Helpers for formatting and printing strings
33c9f3681SJames Bottomley  *
43c9f3681SJames Bottomley  * Copyright 31 August 2008 James Bottomley
516c7fa05SAndy Shevchenko  * Copyright (C) 2013, Intel Corporation
63c9f3681SJames Bottomley  */
73c9f3681SJames Bottomley #include <linux/kernel.h>
83c9f3681SJames Bottomley #include <linux/math64.h>
98bc3bcc9SPaul Gortmaker #include <linux/export.h>
1016c7fa05SAndy Shevchenko #include <linux/ctype.h>
11*c8250381SAndy Shevchenko #include <linux/errno.h>
12*c8250381SAndy Shevchenko #include <linux/string.h>
133c9f3681SJames Bottomley #include <linux/string_helpers.h>
143c9f3681SJames Bottomley 
153c9f3681SJames Bottomley /**
163c9f3681SJames Bottomley  * string_get_size - get the size in the specified units
173c9f3681SJames Bottomley  * @size:	The size to be converted
183c9f3681SJames Bottomley  * @units:	units to use (powers of 1000 or 1024)
193c9f3681SJames Bottomley  * @buf:	buffer to format to
203c9f3681SJames Bottomley  * @len:	length of buffer
213c9f3681SJames Bottomley  *
223c9f3681SJames Bottomley  * This function returns a string formatted to 3 significant figures
233c9f3681SJames Bottomley  * giving the size in the required units.  Returns 0 on success or
243c9f3681SJames Bottomley  * error on failure.  @buf is always zero terminated.
253c9f3681SJames Bottomley  *
263c9f3681SJames Bottomley  */
273c9f3681SJames Bottomley int string_get_size(u64 size, const enum string_size_units units,
283c9f3681SJames Bottomley 		    char *buf, int len)
293c9f3681SJames Bottomley {
30142cda5dSMathias Krause 	static const char *const units_10[] = {
31142cda5dSMathias Krause 		"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB", NULL
32142cda5dSMathias Krause 	};
33142cda5dSMathias Krause 	static const char *const units_2[] = {
34142cda5dSMathias Krause 		"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB",
35142cda5dSMathias Krause 		NULL
36142cda5dSMathias Krause 	};
37142cda5dSMathias Krause 	static const char *const *const units_str[] = {
383c9f3681SJames Bottomley 		[STRING_UNITS_10] = units_10,
393c9f3681SJames Bottomley 		[STRING_UNITS_2] = units_2,
403c9f3681SJames Bottomley 	};
4168aecfb9SAndrew Morton 	static const unsigned int divisor[] = {
423c9f3681SJames Bottomley 		[STRING_UNITS_10] = 1000,
433c9f3681SJames Bottomley 		[STRING_UNITS_2] = 1024,
443c9f3681SJames Bottomley 	};
453c9f3681SJames Bottomley 	int i, j;
463c9f3681SJames Bottomley 	u64 remainder = 0, sf_cap;
473c9f3681SJames Bottomley 	char tmp[8];
483c9f3681SJames Bottomley 
493c9f3681SJames Bottomley 	tmp[0] = '\0';
50a8659597SH. Peter Anvin 	i = 0;
51a8659597SH. Peter Anvin 	if (size >= divisor[units]) {
52a8659597SH. Peter Anvin 		while (size >= divisor[units] && units_str[units][i]) {
533c9f3681SJames Bottomley 			remainder = do_div(size, divisor[units]);
54a8659597SH. Peter Anvin 			i++;
55a8659597SH. Peter Anvin 		}
563c9f3681SJames Bottomley 
573c9f3681SJames Bottomley 		sf_cap = size;
583c9f3681SJames Bottomley 		for (j = 0; sf_cap*10 < 1000; j++)
593c9f3681SJames Bottomley 			sf_cap *= 10;
603c9f3681SJames Bottomley 
613c9f3681SJames Bottomley 		if (j) {
623c9f3681SJames Bottomley 			remainder *= 1000;
633c9f3681SJames Bottomley 			do_div(remainder, divisor[units]);
643c9f3681SJames Bottomley 			snprintf(tmp, sizeof(tmp), ".%03lld",
653c9f3681SJames Bottomley 				 (unsigned long long)remainder);
663c9f3681SJames Bottomley 			tmp[j+1] = '\0';
673c9f3681SJames Bottomley 		}
68a8659597SH. Peter Anvin 	}
693c9f3681SJames Bottomley 
703c9f3681SJames Bottomley 	snprintf(buf, len, "%lld%s %s", (unsigned long long)size,
713c9f3681SJames Bottomley 		 tmp, units_str[units][i]);
723c9f3681SJames Bottomley 
733c9f3681SJames Bottomley 	return 0;
743c9f3681SJames Bottomley }
753c9f3681SJames Bottomley EXPORT_SYMBOL(string_get_size);
7616c7fa05SAndy Shevchenko 
7716c7fa05SAndy Shevchenko static bool unescape_space(char **src, char **dst)
7816c7fa05SAndy Shevchenko {
7916c7fa05SAndy Shevchenko 	char *p = *dst, *q = *src;
8016c7fa05SAndy Shevchenko 
8116c7fa05SAndy Shevchenko 	switch (*q) {
8216c7fa05SAndy Shevchenko 	case 'n':
8316c7fa05SAndy Shevchenko 		*p = '\n';
8416c7fa05SAndy Shevchenko 		break;
8516c7fa05SAndy Shevchenko 	case 'r':
8616c7fa05SAndy Shevchenko 		*p = '\r';
8716c7fa05SAndy Shevchenko 		break;
8816c7fa05SAndy Shevchenko 	case 't':
8916c7fa05SAndy Shevchenko 		*p = '\t';
9016c7fa05SAndy Shevchenko 		break;
9116c7fa05SAndy Shevchenko 	case 'v':
9216c7fa05SAndy Shevchenko 		*p = '\v';
9316c7fa05SAndy Shevchenko 		break;
9416c7fa05SAndy Shevchenko 	case 'f':
9516c7fa05SAndy Shevchenko 		*p = '\f';
9616c7fa05SAndy Shevchenko 		break;
9716c7fa05SAndy Shevchenko 	default:
9816c7fa05SAndy Shevchenko 		return false;
9916c7fa05SAndy Shevchenko 	}
10016c7fa05SAndy Shevchenko 	*dst += 1;
10116c7fa05SAndy Shevchenko 	*src += 1;
10216c7fa05SAndy Shevchenko 	return true;
10316c7fa05SAndy Shevchenko }
10416c7fa05SAndy Shevchenko 
10516c7fa05SAndy Shevchenko static bool unescape_octal(char **src, char **dst)
10616c7fa05SAndy Shevchenko {
10716c7fa05SAndy Shevchenko 	char *p = *dst, *q = *src;
10816c7fa05SAndy Shevchenko 	u8 num;
10916c7fa05SAndy Shevchenko 
11016c7fa05SAndy Shevchenko 	if (isodigit(*q) == 0)
11116c7fa05SAndy Shevchenko 		return false;
11216c7fa05SAndy Shevchenko 
11316c7fa05SAndy Shevchenko 	num = (*q++) & 7;
11416c7fa05SAndy Shevchenko 	while (num < 32 && isodigit(*q) && (q - *src < 3)) {
11516c7fa05SAndy Shevchenko 		num <<= 3;
11616c7fa05SAndy Shevchenko 		num += (*q++) & 7;
11716c7fa05SAndy Shevchenko 	}
11816c7fa05SAndy Shevchenko 	*p = num;
11916c7fa05SAndy Shevchenko 	*dst += 1;
12016c7fa05SAndy Shevchenko 	*src = q;
12116c7fa05SAndy Shevchenko 	return true;
12216c7fa05SAndy Shevchenko }
12316c7fa05SAndy Shevchenko 
12416c7fa05SAndy Shevchenko static bool unescape_hex(char **src, char **dst)
12516c7fa05SAndy Shevchenko {
12616c7fa05SAndy Shevchenko 	char *p = *dst, *q = *src;
12716c7fa05SAndy Shevchenko 	int digit;
12816c7fa05SAndy Shevchenko 	u8 num;
12916c7fa05SAndy Shevchenko 
13016c7fa05SAndy Shevchenko 	if (*q++ != 'x')
13116c7fa05SAndy Shevchenko 		return false;
13216c7fa05SAndy Shevchenko 
13316c7fa05SAndy Shevchenko 	num = digit = hex_to_bin(*q++);
13416c7fa05SAndy Shevchenko 	if (digit < 0)
13516c7fa05SAndy Shevchenko 		return false;
13616c7fa05SAndy Shevchenko 
13716c7fa05SAndy Shevchenko 	digit = hex_to_bin(*q);
13816c7fa05SAndy Shevchenko 	if (digit >= 0) {
13916c7fa05SAndy Shevchenko 		q++;
14016c7fa05SAndy Shevchenko 		num = (num << 4) | digit;
14116c7fa05SAndy Shevchenko 	}
14216c7fa05SAndy Shevchenko 	*p = num;
14316c7fa05SAndy Shevchenko 	*dst += 1;
14416c7fa05SAndy Shevchenko 	*src = q;
14516c7fa05SAndy Shevchenko 	return true;
14616c7fa05SAndy Shevchenko }
14716c7fa05SAndy Shevchenko 
14816c7fa05SAndy Shevchenko static bool unescape_special(char **src, char **dst)
14916c7fa05SAndy Shevchenko {
15016c7fa05SAndy Shevchenko 	char *p = *dst, *q = *src;
15116c7fa05SAndy Shevchenko 
15216c7fa05SAndy Shevchenko 	switch (*q) {
15316c7fa05SAndy Shevchenko 	case '\"':
15416c7fa05SAndy Shevchenko 		*p = '\"';
15516c7fa05SAndy Shevchenko 		break;
15616c7fa05SAndy Shevchenko 	case '\\':
15716c7fa05SAndy Shevchenko 		*p = '\\';
15816c7fa05SAndy Shevchenko 		break;
15916c7fa05SAndy Shevchenko 	case 'a':
16016c7fa05SAndy Shevchenko 		*p = '\a';
16116c7fa05SAndy Shevchenko 		break;
16216c7fa05SAndy Shevchenko 	case 'e':
16316c7fa05SAndy Shevchenko 		*p = '\e';
16416c7fa05SAndy Shevchenko 		break;
16516c7fa05SAndy Shevchenko 	default:
16616c7fa05SAndy Shevchenko 		return false;
16716c7fa05SAndy Shevchenko 	}
16816c7fa05SAndy Shevchenko 	*dst += 1;
16916c7fa05SAndy Shevchenko 	*src += 1;
17016c7fa05SAndy Shevchenko 	return true;
17116c7fa05SAndy Shevchenko }
17216c7fa05SAndy Shevchenko 
173d295634eSAndy Shevchenko /**
174d295634eSAndy Shevchenko  * string_unescape - unquote characters in the given string
175d295634eSAndy Shevchenko  * @src:	source buffer (escaped)
176d295634eSAndy Shevchenko  * @dst:	destination buffer (unescaped)
177d295634eSAndy Shevchenko  * @size:	size of the destination buffer (0 to unlimit)
178d295634eSAndy Shevchenko  * @flags:	combination of the flags (bitwise OR):
179d295634eSAndy Shevchenko  *	%UNESCAPE_SPACE:
180d295634eSAndy Shevchenko  *		'\f' - form feed
181d295634eSAndy Shevchenko  *		'\n' - new line
182d295634eSAndy Shevchenko  *		'\r' - carriage return
183d295634eSAndy Shevchenko  *		'\t' - horizontal tab
184d295634eSAndy Shevchenko  *		'\v' - vertical tab
185d295634eSAndy Shevchenko  *	%UNESCAPE_OCTAL:
186d295634eSAndy Shevchenko  *		'\NNN' - byte with octal value NNN (1 to 3 digits)
187d295634eSAndy Shevchenko  *	%UNESCAPE_HEX:
188d295634eSAndy Shevchenko  *		'\xHH' - byte with hexadecimal value HH (1 to 2 digits)
189d295634eSAndy Shevchenko  *	%UNESCAPE_SPECIAL:
190d295634eSAndy Shevchenko  *		'\"' - double quote
191d295634eSAndy Shevchenko  *		'\\' - backslash
192d295634eSAndy Shevchenko  *		'\a' - alert (BEL)
193d295634eSAndy Shevchenko  *		'\e' - escape
194d295634eSAndy Shevchenko  *	%UNESCAPE_ANY:
195d295634eSAndy Shevchenko  *		all previous together
196d295634eSAndy Shevchenko  *
197d295634eSAndy Shevchenko  * Description:
198d295634eSAndy Shevchenko  * The function unquotes characters in the given string.
199d295634eSAndy Shevchenko  *
200d295634eSAndy Shevchenko  * Because the size of the output will be the same as or less than the size of
201d295634eSAndy Shevchenko  * the input, the transformation may be performed in place.
202d295634eSAndy Shevchenko  *
203d295634eSAndy Shevchenko  * Caller must provide valid source and destination pointers. Be aware that
204d295634eSAndy Shevchenko  * destination buffer will always be NULL-terminated. Source string must be
205d295634eSAndy Shevchenko  * NULL-terminated as well.
206d295634eSAndy Shevchenko  *
207d295634eSAndy Shevchenko  * Return:
208d295634eSAndy Shevchenko  * The amount of the characters processed to the destination buffer excluding
209d295634eSAndy Shevchenko  * trailing '\0' is returned.
210d295634eSAndy Shevchenko  */
21116c7fa05SAndy Shevchenko int string_unescape(char *src, char *dst, size_t size, unsigned int flags)
21216c7fa05SAndy Shevchenko {
21316c7fa05SAndy Shevchenko 	char *out = dst;
21416c7fa05SAndy Shevchenko 
21516c7fa05SAndy Shevchenko 	while (*src && --size) {
21616c7fa05SAndy Shevchenko 		if (src[0] == '\\' && src[1] != '\0' && size > 1) {
21716c7fa05SAndy Shevchenko 			src++;
21816c7fa05SAndy Shevchenko 			size--;
21916c7fa05SAndy Shevchenko 
22016c7fa05SAndy Shevchenko 			if (flags & UNESCAPE_SPACE &&
22116c7fa05SAndy Shevchenko 					unescape_space(&src, &out))
22216c7fa05SAndy Shevchenko 				continue;
22316c7fa05SAndy Shevchenko 
22416c7fa05SAndy Shevchenko 			if (flags & UNESCAPE_OCTAL &&
22516c7fa05SAndy Shevchenko 					unescape_octal(&src, &out))
22616c7fa05SAndy Shevchenko 				continue;
22716c7fa05SAndy Shevchenko 
22816c7fa05SAndy Shevchenko 			if (flags & UNESCAPE_HEX &&
22916c7fa05SAndy Shevchenko 					unescape_hex(&src, &out))
23016c7fa05SAndy Shevchenko 				continue;
23116c7fa05SAndy Shevchenko 
23216c7fa05SAndy Shevchenko 			if (flags & UNESCAPE_SPECIAL &&
23316c7fa05SAndy Shevchenko 					unescape_special(&src, &out))
23416c7fa05SAndy Shevchenko 				continue;
23516c7fa05SAndy Shevchenko 
23616c7fa05SAndy Shevchenko 			*out++ = '\\';
23716c7fa05SAndy Shevchenko 		}
23816c7fa05SAndy Shevchenko 		*out++ = *src++;
23916c7fa05SAndy Shevchenko 	}
24016c7fa05SAndy Shevchenko 	*out = '\0';
24116c7fa05SAndy Shevchenko 
24216c7fa05SAndy Shevchenko 	return out - dst;
24316c7fa05SAndy Shevchenko }
24416c7fa05SAndy Shevchenko EXPORT_SYMBOL(string_unescape);
245*c8250381SAndy Shevchenko 
246*c8250381SAndy Shevchenko static int escape_passthrough(unsigned char c, char **dst, size_t *osz)
247*c8250381SAndy Shevchenko {
248*c8250381SAndy Shevchenko 	char *out = *dst;
249*c8250381SAndy Shevchenko 
250*c8250381SAndy Shevchenko 	if (*osz < 1)
251*c8250381SAndy Shevchenko 		return -ENOMEM;
252*c8250381SAndy Shevchenko 
253*c8250381SAndy Shevchenko 	*out++ = c;
254*c8250381SAndy Shevchenko 
255*c8250381SAndy Shevchenko 	*dst = out;
256*c8250381SAndy Shevchenko 	*osz -= 1;
257*c8250381SAndy Shevchenko 
258*c8250381SAndy Shevchenko 	return 1;
259*c8250381SAndy Shevchenko }
260*c8250381SAndy Shevchenko 
261*c8250381SAndy Shevchenko static int escape_space(unsigned char c, char **dst, size_t *osz)
262*c8250381SAndy Shevchenko {
263*c8250381SAndy Shevchenko 	char *out = *dst;
264*c8250381SAndy Shevchenko 	unsigned char to;
265*c8250381SAndy Shevchenko 
266*c8250381SAndy Shevchenko 	if (*osz < 2)
267*c8250381SAndy Shevchenko 		return -ENOMEM;
268*c8250381SAndy Shevchenko 
269*c8250381SAndy Shevchenko 	switch (c) {
270*c8250381SAndy Shevchenko 	case '\n':
271*c8250381SAndy Shevchenko 		to = 'n';
272*c8250381SAndy Shevchenko 		break;
273*c8250381SAndy Shevchenko 	case '\r':
274*c8250381SAndy Shevchenko 		to = 'r';
275*c8250381SAndy Shevchenko 		break;
276*c8250381SAndy Shevchenko 	case '\t':
277*c8250381SAndy Shevchenko 		to = 't';
278*c8250381SAndy Shevchenko 		break;
279*c8250381SAndy Shevchenko 	case '\v':
280*c8250381SAndy Shevchenko 		to = 'v';
281*c8250381SAndy Shevchenko 		break;
282*c8250381SAndy Shevchenko 	case '\f':
283*c8250381SAndy Shevchenko 		to = 'f';
284*c8250381SAndy Shevchenko 		break;
285*c8250381SAndy Shevchenko 	default:
286*c8250381SAndy Shevchenko 		return 0;
287*c8250381SAndy Shevchenko 	}
288*c8250381SAndy Shevchenko 
289*c8250381SAndy Shevchenko 	*out++ = '\\';
290*c8250381SAndy Shevchenko 	*out++ = to;
291*c8250381SAndy Shevchenko 
292*c8250381SAndy Shevchenko 	*dst = out;
293*c8250381SAndy Shevchenko 	*osz -= 2;
294*c8250381SAndy Shevchenko 
295*c8250381SAndy Shevchenko 	return 1;
296*c8250381SAndy Shevchenko }
297*c8250381SAndy Shevchenko 
298*c8250381SAndy Shevchenko static int escape_special(unsigned char c, char **dst, size_t *osz)
299*c8250381SAndy Shevchenko {
300*c8250381SAndy Shevchenko 	char *out = *dst;
301*c8250381SAndy Shevchenko 	unsigned char to;
302*c8250381SAndy Shevchenko 
303*c8250381SAndy Shevchenko 	if (*osz < 2)
304*c8250381SAndy Shevchenko 		return -ENOMEM;
305*c8250381SAndy Shevchenko 
306*c8250381SAndy Shevchenko 	switch (c) {
307*c8250381SAndy Shevchenko 	case '\\':
308*c8250381SAndy Shevchenko 		to = '\\';
309*c8250381SAndy Shevchenko 		break;
310*c8250381SAndy Shevchenko 	case '\a':
311*c8250381SAndy Shevchenko 		to = 'a';
312*c8250381SAndy Shevchenko 		break;
313*c8250381SAndy Shevchenko 	case '\e':
314*c8250381SAndy Shevchenko 		to = 'e';
315*c8250381SAndy Shevchenko 		break;
316*c8250381SAndy Shevchenko 	default:
317*c8250381SAndy Shevchenko 		return 0;
318*c8250381SAndy Shevchenko 	}
319*c8250381SAndy Shevchenko 
320*c8250381SAndy Shevchenko 	*out++ = '\\';
321*c8250381SAndy Shevchenko 	*out++ = to;
322*c8250381SAndy Shevchenko 
323*c8250381SAndy Shevchenko 	*dst = out;
324*c8250381SAndy Shevchenko 	*osz -= 2;
325*c8250381SAndy Shevchenko 
326*c8250381SAndy Shevchenko 	return 1;
327*c8250381SAndy Shevchenko }
328*c8250381SAndy Shevchenko 
329*c8250381SAndy Shevchenko static int escape_null(unsigned char c, char **dst, size_t *osz)
330*c8250381SAndy Shevchenko {
331*c8250381SAndy Shevchenko 	char *out = *dst;
332*c8250381SAndy Shevchenko 
333*c8250381SAndy Shevchenko 	if (*osz < 2)
334*c8250381SAndy Shevchenko 		return -ENOMEM;
335*c8250381SAndy Shevchenko 
336*c8250381SAndy Shevchenko 	if (c)
337*c8250381SAndy Shevchenko 		return 0;
338*c8250381SAndy Shevchenko 
339*c8250381SAndy Shevchenko 	*out++ = '\\';
340*c8250381SAndy Shevchenko 	*out++ = '0';
341*c8250381SAndy Shevchenko 
342*c8250381SAndy Shevchenko 	*dst = out;
343*c8250381SAndy Shevchenko 	*osz -= 2;
344*c8250381SAndy Shevchenko 
345*c8250381SAndy Shevchenko 	return 1;
346*c8250381SAndy Shevchenko }
347*c8250381SAndy Shevchenko 
348*c8250381SAndy Shevchenko static int escape_octal(unsigned char c, char **dst, size_t *osz)
349*c8250381SAndy Shevchenko {
350*c8250381SAndy Shevchenko 	char *out = *dst;
351*c8250381SAndy Shevchenko 
352*c8250381SAndy Shevchenko 	if (*osz < 4)
353*c8250381SAndy Shevchenko 		return -ENOMEM;
354*c8250381SAndy Shevchenko 
355*c8250381SAndy Shevchenko 	*out++ = '\\';
356*c8250381SAndy Shevchenko 	*out++ = ((c >> 6) & 0x07) + '0';
357*c8250381SAndy Shevchenko 	*out++ = ((c >> 3) & 0x07) + '0';
358*c8250381SAndy Shevchenko 	*out++ = ((c >> 0) & 0x07) + '0';
359*c8250381SAndy Shevchenko 
360*c8250381SAndy Shevchenko 	*dst = out;
361*c8250381SAndy Shevchenko 	*osz -= 4;
362*c8250381SAndy Shevchenko 
363*c8250381SAndy Shevchenko 	return 1;
364*c8250381SAndy Shevchenko }
365*c8250381SAndy Shevchenko 
366*c8250381SAndy Shevchenko static int escape_hex(unsigned char c, char **dst, size_t *osz)
367*c8250381SAndy Shevchenko {
368*c8250381SAndy Shevchenko 	char *out = *dst;
369*c8250381SAndy Shevchenko 
370*c8250381SAndy Shevchenko 	if (*osz < 4)
371*c8250381SAndy Shevchenko 		return -ENOMEM;
372*c8250381SAndy Shevchenko 
373*c8250381SAndy Shevchenko 	*out++ = '\\';
374*c8250381SAndy Shevchenko 	*out++ = 'x';
375*c8250381SAndy Shevchenko 	*out++ = hex_asc_hi(c);
376*c8250381SAndy Shevchenko 	*out++ = hex_asc_lo(c);
377*c8250381SAndy Shevchenko 
378*c8250381SAndy Shevchenko 	*dst = out;
379*c8250381SAndy Shevchenko 	*osz -= 4;
380*c8250381SAndy Shevchenko 
381*c8250381SAndy Shevchenko 	return 1;
382*c8250381SAndy Shevchenko }
383*c8250381SAndy Shevchenko 
384*c8250381SAndy Shevchenko /**
385*c8250381SAndy Shevchenko  * string_escape_mem - quote characters in the given memory buffer
386*c8250381SAndy Shevchenko  * @src:	source buffer (unescaped)
387*c8250381SAndy Shevchenko  * @isz:	source buffer size
388*c8250381SAndy Shevchenko  * @dst:	destination buffer (escaped)
389*c8250381SAndy Shevchenko  * @osz:	destination buffer size
390*c8250381SAndy Shevchenko  * @flags:	combination of the flags (bitwise OR):
391*c8250381SAndy Shevchenko  *	%ESCAPE_SPACE:
392*c8250381SAndy Shevchenko  *		'\f' - form feed
393*c8250381SAndy Shevchenko  *		'\n' - new line
394*c8250381SAndy Shevchenko  *		'\r' - carriage return
395*c8250381SAndy Shevchenko  *		'\t' - horizontal tab
396*c8250381SAndy Shevchenko  *		'\v' - vertical tab
397*c8250381SAndy Shevchenko  *	%ESCAPE_SPECIAL:
398*c8250381SAndy Shevchenko  *		'\\' - backslash
399*c8250381SAndy Shevchenko  *		'\a' - alert (BEL)
400*c8250381SAndy Shevchenko  *		'\e' - escape
401*c8250381SAndy Shevchenko  *	%ESCAPE_NULL:
402*c8250381SAndy Shevchenko  *		'\0' - null
403*c8250381SAndy Shevchenko  *	%ESCAPE_OCTAL:
404*c8250381SAndy Shevchenko  *		'\NNN' - byte with octal value NNN (3 digits)
405*c8250381SAndy Shevchenko  *	%ESCAPE_ANY:
406*c8250381SAndy Shevchenko  *		all previous together
407*c8250381SAndy Shevchenko  *	%ESCAPE_NP:
408*c8250381SAndy Shevchenko  *		escape only non-printable characters (checked by isprint)
409*c8250381SAndy Shevchenko  *	%ESCAPE_ANY_NP:
410*c8250381SAndy Shevchenko  *		all previous together
411*c8250381SAndy Shevchenko  *	%ESCAPE_HEX:
412*c8250381SAndy Shevchenko  *		'\xHH' - byte with hexadecimal value HH (2 digits)
413*c8250381SAndy Shevchenko  * @esc:	NULL-terminated string of characters any of which, if found in
414*c8250381SAndy Shevchenko  *		the source, has to be escaped
415*c8250381SAndy Shevchenko  *
416*c8250381SAndy Shevchenko  * Description:
417*c8250381SAndy Shevchenko  * The process of escaping byte buffer includes several parts. They are applied
418*c8250381SAndy Shevchenko  * in the following sequence.
419*c8250381SAndy Shevchenko  *	1. The character is matched to the printable class, if asked, and in
420*c8250381SAndy Shevchenko  *	   case of match it passes through to the output.
421*c8250381SAndy Shevchenko  *	2. The character is not matched to the one from @esc string and thus
422*c8250381SAndy Shevchenko  *	   must go as is to the output.
423*c8250381SAndy Shevchenko  *	3. The character is checked if it falls into the class given by @flags.
424*c8250381SAndy Shevchenko  *	   %ESCAPE_OCTAL and %ESCAPE_HEX are going last since they cover any
425*c8250381SAndy Shevchenko  *	   character. Note that they actually can't go together, otherwise
426*c8250381SAndy Shevchenko  *	   %ESCAPE_HEX will be ignored.
427*c8250381SAndy Shevchenko  *
428*c8250381SAndy Shevchenko  * Caller must provide valid source and destination pointers. Be aware that
429*c8250381SAndy Shevchenko  * destination buffer will not be NULL-terminated, thus caller have to append
430*c8250381SAndy Shevchenko  * it if needs.
431*c8250381SAndy Shevchenko  *
432*c8250381SAndy Shevchenko  * Return:
433*c8250381SAndy Shevchenko  * The amount of the characters processed to the destination buffer, or
434*c8250381SAndy Shevchenko  * %-ENOMEM if the size of buffer is not enough to put an escaped character is
435*c8250381SAndy Shevchenko  * returned.
436*c8250381SAndy Shevchenko  *
437*c8250381SAndy Shevchenko  * Even in the case of error @dst pointer will be updated to point to the byte
438*c8250381SAndy Shevchenko  * after the last processed character.
439*c8250381SAndy Shevchenko  */
440*c8250381SAndy Shevchenko int string_escape_mem(const char *src, size_t isz, char **dst, size_t osz,
441*c8250381SAndy Shevchenko 		      unsigned int flags, const char *esc)
442*c8250381SAndy Shevchenko {
443*c8250381SAndy Shevchenko 	char *out = *dst, *p = out;
444*c8250381SAndy Shevchenko 	bool is_dict = esc && *esc;
445*c8250381SAndy Shevchenko 	int ret = 0;
446*c8250381SAndy Shevchenko 
447*c8250381SAndy Shevchenko 	while (isz--) {
448*c8250381SAndy Shevchenko 		unsigned char c = *src++;
449*c8250381SAndy Shevchenko 
450*c8250381SAndy Shevchenko 		/*
451*c8250381SAndy Shevchenko 		 * Apply rules in the following sequence:
452*c8250381SAndy Shevchenko 		 *	- the character is printable, when @flags has
453*c8250381SAndy Shevchenko 		 *	  %ESCAPE_NP bit set
454*c8250381SAndy Shevchenko 		 *	- the @esc string is supplied and does not contain a
455*c8250381SAndy Shevchenko 		 *	  character under question
456*c8250381SAndy Shevchenko 		 *	- the character doesn't fall into a class of symbols
457*c8250381SAndy Shevchenko 		 *	  defined by given @flags
458*c8250381SAndy Shevchenko 		 * In these cases we just pass through a character to the
459*c8250381SAndy Shevchenko 		 * output buffer.
460*c8250381SAndy Shevchenko 		 */
461*c8250381SAndy Shevchenko 		if ((flags & ESCAPE_NP && isprint(c)) ||
462*c8250381SAndy Shevchenko 		    (is_dict && !strchr(esc, c))) {
463*c8250381SAndy Shevchenko 			/* do nothing */
464*c8250381SAndy Shevchenko 		} else {
465*c8250381SAndy Shevchenko 			if (flags & ESCAPE_SPACE) {
466*c8250381SAndy Shevchenko 				ret = escape_space(c, &p, &osz);
467*c8250381SAndy Shevchenko 				if (ret < 0)
468*c8250381SAndy Shevchenko 					break;
469*c8250381SAndy Shevchenko 				if (ret > 0)
470*c8250381SAndy Shevchenko 					continue;
471*c8250381SAndy Shevchenko 			}
472*c8250381SAndy Shevchenko 
473*c8250381SAndy Shevchenko 			if (flags & ESCAPE_SPECIAL) {
474*c8250381SAndy Shevchenko 				ret = escape_special(c, &p, &osz);
475*c8250381SAndy Shevchenko 				if (ret < 0)
476*c8250381SAndy Shevchenko 					break;
477*c8250381SAndy Shevchenko 				if (ret > 0)
478*c8250381SAndy Shevchenko 					continue;
479*c8250381SAndy Shevchenko 			}
480*c8250381SAndy Shevchenko 
481*c8250381SAndy Shevchenko 			if (flags & ESCAPE_NULL) {
482*c8250381SAndy Shevchenko 				ret = escape_null(c, &p, &osz);
483*c8250381SAndy Shevchenko 				if (ret < 0)
484*c8250381SAndy Shevchenko 					break;
485*c8250381SAndy Shevchenko 				if (ret > 0)
486*c8250381SAndy Shevchenko 					continue;
487*c8250381SAndy Shevchenko 			}
488*c8250381SAndy Shevchenko 
489*c8250381SAndy Shevchenko 			/* ESCAPE_OCTAL and ESCAPE_HEX always go last */
490*c8250381SAndy Shevchenko 			if (flags & ESCAPE_OCTAL) {
491*c8250381SAndy Shevchenko 				ret = escape_octal(c, &p, &osz);
492*c8250381SAndy Shevchenko 				if (ret < 0)
493*c8250381SAndy Shevchenko 					break;
494*c8250381SAndy Shevchenko 				continue;
495*c8250381SAndy Shevchenko 			}
496*c8250381SAndy Shevchenko 			if (flags & ESCAPE_HEX) {
497*c8250381SAndy Shevchenko 				ret = escape_hex(c, &p, &osz);
498*c8250381SAndy Shevchenko 				if (ret < 0)
499*c8250381SAndy Shevchenko 					break;
500*c8250381SAndy Shevchenko 				continue;
501*c8250381SAndy Shevchenko 			}
502*c8250381SAndy Shevchenko 		}
503*c8250381SAndy Shevchenko 
504*c8250381SAndy Shevchenko 		ret = escape_passthrough(c, &p, &osz);
505*c8250381SAndy Shevchenko 		if (ret < 0)
506*c8250381SAndy Shevchenko 			break;
507*c8250381SAndy Shevchenko 	}
508*c8250381SAndy Shevchenko 
509*c8250381SAndy Shevchenko 	*dst = p;
510*c8250381SAndy Shevchenko 
511*c8250381SAndy Shevchenko 	if (ret < 0)
512*c8250381SAndy Shevchenko 		return ret;
513*c8250381SAndy Shevchenko 
514*c8250381SAndy Shevchenko 	return p - out;
515*c8250381SAndy Shevchenko }
516*c8250381SAndy Shevchenko EXPORT_SYMBOL(string_escape_mem);
517