You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
516 lines
10 KiB
516 lines
10 KiB
/* |
|
* 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; |
|
} |
|
} |
|
|
|
ret = escape_passthrough(c, &p, &osz); |
|
if (ret < 0) |
|
break; |
|
} |
|
|
|
*dst = p; |
|
|
|
if (ret < 0) |
|
return ret; |
|
|
|
return p - out; |
|
} |
|
EXPORT_SYMBOL(string_escape_mem);
|
|
|