linux/lib/string_helpers.c
<<
>>
Prefs
   1/*
   2 * Helpers for formatting and printing strings
   3 *
   4 * Copyright 31 August 2008 James Bottomley
   5 * Copyright (C) 2013, Intel Corporation
   6 */
   7#include <linux/bug.h>
   8#include <linux/kernel.h>
   9#include <linux/math64.h>
  10#include <linux/export.h>
  11#include <linux/ctype.h>
  12#include <linux/string_helpers.h>
  13
  14/**
  15 * string_get_size - get the size in the specified units
  16 * @size:       The size to be converted in blocks
  17 * @blk_size:   Size of the block (use 1 for size in bytes)
  18 * @units:      units to use (powers of 1000 or 1024)
  19 * @buf:        buffer to format to
  20 * @len:        length of buffer
  21 *
  22 * This function returns a string formatted to 3 significant figures
  23 * giving the size in the required units.  Returns 0 on success or
  24 * error on failure.  @buf is always zero terminated.
  25 *
  26 */
  27int string_get_size(u64 size, u64 blk_size, const enum string_size_units units,
  28                    char *buf, int len)
  29{
  30        static const char *units_10[] = { "B", "kB", "MB", "GB", "TB", "PB",
  31                                   "EB", "ZB", "YB", NULL};
  32        static const char *units_2[] = {"B", "KiB", "MiB", "GiB", "TiB", "PiB",
  33                                 "EiB", "ZiB", "YiB", NULL };
  34        static const char **units_str[] = {
  35                [STRING_UNITS_10] =  units_10,
  36                [STRING_UNITS_2] = units_2,
  37        };
  38        static const unsigned int divisor[] = {
  39                [STRING_UNITS_10] = 1000,
  40                [STRING_UNITS_2] = 1024,
  41        };
  42        int i, j;
  43        u32 remainder = 0, sf_cap, exp;
  44        char tmp[8];
  45        const char *unit;
  46
  47        tmp[0] = '\0';
  48        i = 0;
  49        if (!size)
  50                goto out;
  51
  52        while (blk_size >= divisor[units]) {
  53                remainder = do_div(blk_size, divisor[units]);
  54                i++;
  55        }
  56
  57        exp = divisor[units] / (u32)blk_size;
  58        /*
  59         * size must be strictly greater than exp here to ensure that remainder
  60         * is greater than divisor[units] coming out of the if below.
  61         */
  62        if (size > exp) {
  63                remainder = do_div(size, divisor[units]);
  64                remainder *= blk_size;
  65                i++;
  66        } else {
  67                remainder *= size;
  68        }
  69
  70        size *= blk_size;
  71        size += remainder / divisor[units];
  72        remainder %= divisor[units];
  73
  74        while (size >= divisor[units]) {
  75                remainder = do_div(size, divisor[units]);
  76                i++;
  77        }
  78
  79        sf_cap = size;
  80        for (j = 0; sf_cap*10 < 1000; j++)
  81                sf_cap *= 10;
  82
  83        if (j) {
  84                remainder *= 1000;
  85                remainder /= divisor[units];
  86                snprintf(tmp, sizeof(tmp), ".%03u", remainder);
  87                tmp[j+1] = '\0';
  88        }
  89
  90 out:
  91        if (i >= ARRAY_SIZE(units_2))
  92                unit = "UNK";
  93        else
  94                unit = units_str[units][i];
  95
  96        snprintf(buf, len, "%lld%s %s", (unsigned long long)size,
  97                 tmp, unit);
  98
  99        return 0;
 100}
 101EXPORT_SYMBOL(string_get_size);
 102
 103static bool unescape_space(char **src, char **dst)
 104{
 105        char *p = *dst, *q = *src;
 106
 107        switch (*q) {
 108        case 'n':
 109                *p = '\n';
 110                break;
 111        case 'r':
 112                *p = '\r';
 113                break;
 114        case 't':
 115                *p = '\t';
 116                break;
 117        case 'v':
 118                *p = '\v';
 119                break;
 120        case 'f':
 121                *p = '\f';
 122                break;
 123        default:
 124                return false;
 125        }
 126        *dst += 1;
 127        *src += 1;
 128        return true;
 129}
 130
 131static bool unescape_octal(char **src, char **dst)
 132{
 133        char *p = *dst, *q = *src;
 134        u8 num;
 135
 136        if (isodigit(*q) == 0)
 137                return false;
 138
 139        num = (*q++) & 7;
 140        while (num < 32 && isodigit(*q) && (q - *src < 3)) {
 141                num <<= 3;
 142                num += (*q++) & 7;
 143        }
 144        *p = num;
 145        *dst += 1;
 146        *src = q;
 147        return true;
 148}
 149
 150static bool unescape_hex(char **src, char **dst)
 151{
 152        char *p = *dst, *q = *src;
 153        int digit;
 154        u8 num;
 155
 156        if (*q++ != 'x')
 157                return false;
 158
 159        num = digit = hex_to_bin(*q++);
 160        if (digit < 0)
 161                return false;
 162
 163        digit = hex_to_bin(*q);
 164        if (digit >= 0) {
 165                q++;
 166                num = (num << 4) | digit;
 167        }
 168        *p = num;
 169        *dst += 1;
 170        *src = q;
 171        return true;
 172}
 173
 174static bool unescape_special(char **src, char **dst)
 175{
 176        char *p = *dst, *q = *src;
 177
 178        switch (*q) {
 179        case '\"':
 180                *p = '\"';
 181                break;
 182        case '\\':
 183                *p = '\\';
 184                break;
 185        case 'a':
 186                *p = '\a';
 187                break;
 188        case 'e':
 189                *p = '\e';
 190                break;
 191        default:
 192                return false;
 193        }
 194        *dst += 1;
 195        *src += 1;
 196        return true;
 197}
 198
 199int string_unescape(char *src, char *dst, size_t size, unsigned int flags)
 200{
 201        char *out = dst;
 202
 203        while (*src && --size) {
 204                if (src[0] == '\\' && src[1] != '\0' && size > 1) {
 205                        src++;
 206                        size--;
 207
 208                        if (flags & UNESCAPE_SPACE &&
 209                                        unescape_space(&src, &out))
 210                                continue;
 211
 212                        if (flags & UNESCAPE_OCTAL &&
 213                                        unescape_octal(&src, &out))
 214                                continue;
 215
 216                        if (flags & UNESCAPE_HEX &&
 217                                        unescape_hex(&src, &out))
 218                                continue;
 219
 220                        if (flags & UNESCAPE_SPECIAL &&
 221                                        unescape_special(&src, &out))
 222                                continue;
 223
 224                        *out++ = '\\';
 225                }
 226                *out++ = *src++;
 227        }
 228        *out = '\0';
 229
 230        return out - dst;
 231}
 232EXPORT_SYMBOL(string_unescape);
 233