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