/* * Helpers for formatting and printing strings * * Copyright 31 August 2008 James Bottomley * Copyright (C) 2013, Intel Corporation */ #include #include #include #include #include #include #include #include /** * string_get_size - get the size in the specified units * @size: The size to be converted in blocks * @blk_size: Size of the block (use 1 for size in bytes) * @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. @buf should have room for * at least 9 bytes and will always be zero terminated. * */ void string_get_size(u64 size, u64 blk_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" }; static const char *const units_2[] = { "B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB" }; 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; u32 remainder = 0, sf_cap, exp; char tmp[8]; const char *unit; tmp[0] = '\0'; i = 0; if (!size) goto out; while (blk_size >= divisor[units]) { remainder = do_div(blk_size, divisor[units]); i++; } exp = divisor[units] / (u32)blk_size; if (size >= exp) { remainder = do_div(size, divisor[units]); remainder *= blk_size; i++; } else { remainder *= size; } size *= blk_size; size += remainder / divisor[units]; remainder %= divisor[units]; while (size >= divisor[units]) { 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; remainder /= divisor[units]; snprintf(tmp, sizeof(tmp), ".%03u", remainder); tmp[j+1] = '\0'; } out: if (i >= ARRAY_SIZE(units_2)) unit = "UNK"; else unit = units_str[units][i]; snprintf(buf, len, "%u%s %s", (u32)size, tmp, unit); } 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);