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