diff options
Diffstat (limited to 'lib/string_helpers.c')
| -rw-r--r-- | lib/string_helpers.c | 261 |
1 files changed, 128 insertions, 133 deletions
diff --git a/lib/string_helpers.c b/lib/string_helpers.c index 8f8c4417f228..c98ae818eb4e 100644 --- a/lib/string_helpers.c +++ b/lib/string_helpers.c | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | * Copyright 31 August 2008 James Bottomley | 4 | * Copyright 31 August 2008 James Bottomley |
| 5 | * Copyright (C) 2013, Intel Corporation | 5 | * Copyright (C) 2013, Intel Corporation |
| 6 | */ | 6 | */ |
| 7 | #include <linux/bug.h> | ||
| 7 | #include <linux/kernel.h> | 8 | #include <linux/kernel.h> |
| 8 | #include <linux/math64.h> | 9 | #include <linux/math64.h> |
| 9 | #include <linux/export.h> | 10 | #include <linux/export.h> |
| @@ -14,7 +15,8 @@ | |||
| 14 | 15 | ||
| 15 | /** | 16 | /** |
| 16 | * string_get_size - get the size in the specified units | 17 | * string_get_size - get the size in the specified units |
| 17 | * @size: The size to be converted | 18 | * @size: The size to be converted in blocks |
| 19 | * @blk_size: Size of the block (use 1 for size in bytes) | ||
| 18 | * @units: units to use (powers of 1000 or 1024) | 20 | * @units: units to use (powers of 1000 or 1024) |
| 19 | * @buf: buffer to format to | 21 | * @buf: buffer to format to |
| 20 | * @len: length of buffer | 22 | * @len: length of buffer |
| @@ -24,14 +26,14 @@ | |||
| 24 | * at least 9 bytes and will always be zero terminated. | 26 | * at least 9 bytes and will always be zero terminated. |
| 25 | * | 27 | * |
| 26 | */ | 28 | */ |
| 27 | void string_get_size(u64 size, const enum string_size_units units, | 29 | void string_get_size(u64 size, u64 blk_size, const enum string_size_units units, |
| 28 | char *buf, int len) | 30 | char *buf, int len) |
| 29 | { | 31 | { |
| 30 | static const char *const units_10[] = { | 32 | static const char *const units_10[] = { |
| 31 | "B", "kB", "MB", "GB", "TB", "PB", "EB" | 33 | "B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" |
| 32 | }; | 34 | }; |
| 33 | static const char *const units_2[] = { | 35 | static const char *const units_2[] = { |
| 34 | "B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB" | 36 | "B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB" |
| 35 | }; | 37 | }; |
| 36 | static const char *const *const units_str[] = { | 38 | static const char *const *const units_str[] = { |
| 37 | [STRING_UNITS_10] = units_10, | 39 | [STRING_UNITS_10] = units_10, |
| @@ -42,31 +44,57 @@ void string_get_size(u64 size, const enum string_size_units units, | |||
| 42 | [STRING_UNITS_2] = 1024, | 44 | [STRING_UNITS_2] = 1024, |
| 43 | }; | 45 | }; |
| 44 | int i, j; | 46 | int i, j; |
| 45 | u32 remainder = 0, sf_cap; | 47 | u32 remainder = 0, sf_cap, exp; |
| 46 | char tmp[8]; | 48 | char tmp[8]; |
| 49 | const char *unit; | ||
| 47 | 50 | ||
| 48 | tmp[0] = '\0'; | 51 | tmp[0] = '\0'; |
| 49 | i = 0; | 52 | i = 0; |
| 50 | if (size >= divisor[units]) { | 53 | if (!size) |
| 51 | while (size >= divisor[units]) { | 54 | goto out; |
| 52 | remainder = do_div(size, divisor[units]); | ||
| 53 | i++; | ||
| 54 | } | ||
| 55 | 55 | ||
| 56 | sf_cap = size; | 56 | while (blk_size >= divisor[units]) { |
| 57 | for (j = 0; sf_cap*10 < 1000; j++) | 57 | remainder = do_div(blk_size, divisor[units]); |
| 58 | sf_cap *= 10; | 58 | i++; |
| 59 | } | ||
| 59 | 60 | ||
| 60 | if (j) { | 61 | exp = divisor[units] / (u32)blk_size; |
| 61 | remainder *= 1000; | 62 | if (size >= exp) { |
| 62 | remainder /= divisor[units]; | 63 | remainder = do_div(size, divisor[units]); |
| 63 | snprintf(tmp, sizeof(tmp), ".%03u", remainder); | 64 | remainder *= blk_size; |
| 64 | tmp[j+1] = '\0'; | 65 | i++; |
| 65 | } | 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++; | ||
| 66 | } | 77 | } |
| 67 | 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 | |||
| 68 | snprintf(buf, len, "%u%s %s", (u32)size, | 96 | snprintf(buf, len, "%u%s %s", (u32)size, |
| 69 | tmp, units_str[units][i]); | 97 | tmp, unit); |
| 70 | } | 98 | } |
| 71 | EXPORT_SYMBOL(string_get_size); | 99 | EXPORT_SYMBOL(string_get_size); |
| 72 | 100 | ||
| @@ -239,29 +267,21 @@ int string_unescape(char *src, char *dst, size_t size, unsigned int flags) | |||
| 239 | } | 267 | } |
| 240 | EXPORT_SYMBOL(string_unescape); | 268 | EXPORT_SYMBOL(string_unescape); |
| 241 | 269 | ||
| 242 | static int escape_passthrough(unsigned char c, char **dst, size_t *osz) | 270 | static bool escape_passthrough(unsigned char c, char **dst, char *end) |
| 243 | { | 271 | { |
| 244 | char *out = *dst; | 272 | char *out = *dst; |
| 245 | 273 | ||
| 246 | if (*osz < 1) | 274 | if (out < end) |
| 247 | return -ENOMEM; | 275 | *out = c; |
| 248 | 276 | *dst = out + 1; | |
| 249 | *out++ = c; | 277 | return true; |
| 250 | |||
| 251 | *dst = out; | ||
| 252 | *osz -= 1; | ||
| 253 | |||
| 254 | return 1; | ||
| 255 | } | 278 | } |
| 256 | 279 | ||
| 257 | static int escape_space(unsigned char c, char **dst, size_t *osz) | 280 | static bool escape_space(unsigned char c, char **dst, char *end) |
| 258 | { | 281 | { |
| 259 | char *out = *dst; | 282 | char *out = *dst; |
| 260 | unsigned char to; | 283 | unsigned char to; |
| 261 | 284 | ||
| 262 | if (*osz < 2) | ||
| 263 | return -ENOMEM; | ||
| 264 | |||
| 265 | switch (c) { | 285 | switch (c) { |
| 266 | case '\n': | 286 | case '\n': |
| 267 | to = 'n'; | 287 | to = 'n'; |
| @@ -279,26 +299,25 @@ static int escape_space(unsigned char c, char **dst, size_t *osz) | |||
| 279 | to = 'f'; | 299 | to = 'f'; |
| 280 | break; | 300 | break; |
| 281 | default: | 301 | default: |
| 282 | return 0; | 302 | return false; |
| 283 | } | 303 | } |
| 284 | 304 | ||
| 285 | *out++ = '\\'; | 305 | if (out < end) |
| 286 | *out++ = to; | 306 | *out = '\\'; |
| 307 | ++out; | ||
| 308 | if (out < end) | ||
| 309 | *out = to; | ||
| 310 | ++out; | ||
| 287 | 311 | ||
| 288 | *dst = out; | 312 | *dst = out; |
| 289 | *osz -= 2; | 313 | return true; |
| 290 | |||
| 291 | return 1; | ||
| 292 | } | 314 | } |
| 293 | 315 | ||
| 294 | static int escape_special(unsigned char c, char **dst, size_t *osz) | 316 | static bool escape_special(unsigned char c, char **dst, char *end) |
| 295 | { | 317 | { |
| 296 | char *out = *dst; | 318 | char *out = *dst; |
| 297 | unsigned char to; | 319 | unsigned char to; |
| 298 | 320 | ||
| 299 | if (*osz < 2) | ||
| 300 | return -ENOMEM; | ||
| 301 | |||
| 302 | switch (c) { | 321 | switch (c) { |
| 303 | case '\\': | 322 | case '\\': |
| 304 | to = '\\'; | 323 | to = '\\'; |
| @@ -310,71 +329,78 @@ static int escape_special(unsigned char c, char **dst, size_t *osz) | |||
| 310 | to = 'e'; | 329 | to = 'e'; |
| 311 | break; | 330 | break; |
| 312 | default: | 331 | default: |
| 313 | return 0; | 332 | return false; |
| 314 | } | 333 | } |
| 315 | 334 | ||
| 316 | *out++ = '\\'; | 335 | if (out < end) |
| 317 | *out++ = to; | 336 | *out = '\\'; |
| 337 | ++out; | ||
| 338 | if (out < end) | ||
| 339 | *out = to; | ||
| 340 | ++out; | ||
| 318 | 341 | ||
| 319 | *dst = out; | 342 | *dst = out; |
| 320 | *osz -= 2; | 343 | return true; |
| 321 | |||
| 322 | return 1; | ||
| 323 | } | 344 | } |
| 324 | 345 | ||
| 325 | static int escape_null(unsigned char c, char **dst, size_t *osz) | 346 | static bool escape_null(unsigned char c, char **dst, char *end) |
| 326 | { | 347 | { |
| 327 | char *out = *dst; | 348 | char *out = *dst; |
| 328 | 349 | ||
| 329 | if (*osz < 2) | ||
| 330 | return -ENOMEM; | ||
| 331 | |||
| 332 | if (c) | 350 | if (c) |
| 333 | return 0; | 351 | return false; |
| 334 | 352 | ||
| 335 | *out++ = '\\'; | 353 | if (out < end) |
| 336 | *out++ = '0'; | 354 | *out = '\\'; |
| 355 | ++out; | ||
| 356 | if (out < end) | ||
| 357 | *out = '0'; | ||
| 358 | ++out; | ||
| 337 | 359 | ||
| 338 | *dst = out; | 360 | *dst = out; |
| 339 | *osz -= 2; | 361 | return true; |
| 340 | |||
| 341 | return 1; | ||
| 342 | } | 362 | } |
| 343 | 363 | ||
| 344 | static int escape_octal(unsigned char c, char **dst, size_t *osz) | 364 | static bool escape_octal(unsigned char c, char **dst, char *end) |
| 345 | { | 365 | { |
| 346 | char *out = *dst; | 366 | char *out = *dst; |
| 347 | 367 | ||
| 348 | if (*osz < 4) | 368 | if (out < end) |
| 349 | return -ENOMEM; | 369 | *out = '\\'; |
| 350 | 370 | ++out; | |
| 351 | *out++ = '\\'; | 371 | if (out < end) |
| 352 | *out++ = ((c >> 6) & 0x07) + '0'; | 372 | *out = ((c >> 6) & 0x07) + '0'; |
| 353 | *out++ = ((c >> 3) & 0x07) + '0'; | 373 | ++out; |
| 354 | *out++ = ((c >> 0) & 0x07) + '0'; | 374 | if (out < end) |
| 375 | *out = ((c >> 3) & 0x07) + '0'; | ||
| 376 | ++out; | ||
| 377 | if (out < end) | ||
| 378 | *out = ((c >> 0) & 0x07) + '0'; | ||
| 379 | ++out; | ||
| 355 | 380 | ||
| 356 | *dst = out; | 381 | *dst = out; |
| 357 | *osz -= 4; | 382 | return true; |
| 358 | |||
| 359 | return 1; | ||
| 360 | } | 383 | } |
| 361 | 384 | ||
| 362 | static int escape_hex(unsigned char c, char **dst, size_t *osz) | 385 | static bool escape_hex(unsigned char c, char **dst, char *end) |
| 363 | { | 386 | { |
| 364 | char *out = *dst; | 387 | char *out = *dst; |
| 365 | 388 | ||
| 366 | if (*osz < 4) | 389 | if (out < end) |
| 367 | return -ENOMEM; | 390 | *out = '\\'; |
| 368 | 391 | ++out; | |
| 369 | *out++ = '\\'; | 392 | if (out < end) |
| 370 | *out++ = 'x'; | 393 | *out = 'x'; |
| 371 | *out++ = hex_asc_hi(c); | 394 | ++out; |
| 372 | *out++ = hex_asc_lo(c); | 395 | if (out < end) |
| 396 | *out = hex_asc_hi(c); | ||
| 397 | ++out; | ||
| 398 | if (out < end) | ||
| 399 | *out = hex_asc_lo(c); | ||
| 400 | ++out; | ||
| 373 | 401 | ||
| 374 | *dst = out; | 402 | *dst = out; |
| 375 | *osz -= 4; | 403 | return true; |
| 376 | |||
| 377 | return 1; | ||
| 378 | } | 404 | } |
| 379 | 405 | ||
| 380 | /** | 406 | /** |
| @@ -426,19 +452,17 @@ static int escape_hex(unsigned char c, char **dst, size_t *osz) | |||
| 426 | * it if needs. | 452 | * it if needs. |
| 427 | * | 453 | * |
| 428 | * Return: | 454 | * Return: |
| 429 | * The amount of the characters processed to the destination buffer, or | 455 | * The total size of the escaped output that would be generated for |
| 430 | * %-ENOMEM if the size of buffer is not enough to put an escaped character is | 456 | * the given input and flags. To check whether the output was |
| 431 | * returned. | 457 | * truncated, compare the return value to osz. There is room left in |
| 432 | * | 458 | * dst for a '\0' terminator if and only if ret < osz. |
| 433 | * Even in the case of error @dst pointer will be updated to point to the byte | ||
| 434 | * after the last processed character. | ||
| 435 | */ | 459 | */ |
| 436 | int string_escape_mem(const char *src, size_t isz, char **dst, size_t osz, | 460 | int string_escape_mem(const char *src, size_t isz, char *dst, size_t osz, |
| 437 | unsigned int flags, const char *esc) | 461 | unsigned int flags, const char *esc) |
| 438 | { | 462 | { |
| 439 | char *out = *dst, *p = out; | 463 | char *p = dst; |
| 464 | char *end = p + osz; | ||
| 440 | bool is_dict = esc && *esc; | 465 | bool is_dict = esc && *esc; |
| 441 | int ret = 0; | ||
| 442 | 466 | ||
| 443 | while (isz--) { | 467 | while (isz--) { |
| 444 | unsigned char c = *src++; | 468 | unsigned char c = *src++; |
| @@ -458,55 +482,26 @@ int string_escape_mem(const char *src, size_t isz, char **dst, size_t osz, | |||
| 458 | (is_dict && !strchr(esc, c))) { | 482 | (is_dict && !strchr(esc, c))) { |
| 459 | /* do nothing */ | 483 | /* do nothing */ |
| 460 | } else { | 484 | } else { |
| 461 | if (flags & ESCAPE_SPACE) { | 485 | if (flags & ESCAPE_SPACE && escape_space(c, &p, end)) |
| 462 | ret = escape_space(c, &p, &osz); | 486 | continue; |
| 463 | if (ret < 0) | 487 | |
| 464 | break; | 488 | if (flags & ESCAPE_SPECIAL && escape_special(c, &p, end)) |
| 465 | if (ret > 0) | 489 | continue; |
| 466 | continue; | 490 | |
| 467 | } | 491 | if (flags & ESCAPE_NULL && escape_null(c, &p, end)) |
| 468 | 492 | continue; | |
| 469 | if (flags & ESCAPE_SPECIAL) { | ||
| 470 | ret = escape_special(c, &p, &osz); | ||
| 471 | if (ret < 0) | ||
| 472 | break; | ||
| 473 | if (ret > 0) | ||
| 474 | continue; | ||
| 475 | } | ||
| 476 | |||
| 477 | if (flags & ESCAPE_NULL) { | ||
| 478 | ret = escape_null(c, &p, &osz); | ||
| 479 | if (ret < 0) | ||
| 480 | break; | ||
| 481 | if (ret > 0) | ||
| 482 | continue; | ||
| 483 | } | ||
| 484 | 493 | ||
| 485 | /* ESCAPE_OCTAL and ESCAPE_HEX always go last */ | 494 | /* ESCAPE_OCTAL and ESCAPE_HEX always go last */ |
| 486 | if (flags & ESCAPE_OCTAL) { | 495 | if (flags & ESCAPE_OCTAL && escape_octal(c, &p, end)) |
| 487 | ret = escape_octal(c, &p, &osz); | ||
| 488 | if (ret < 0) | ||
| 489 | break; | ||
| 490 | continue; | 496 | continue; |
| 491 | } | 497 | |
| 492 | if (flags & ESCAPE_HEX) { | 498 | if (flags & ESCAPE_HEX && escape_hex(c, &p, end)) |
| 493 | ret = escape_hex(c, &p, &osz); | ||
| 494 | if (ret < 0) | ||
| 495 | break; | ||
| 496 | continue; | 499 | continue; |
| 497 | } | ||
| 498 | } | 500 | } |
| 499 | 501 | ||
| 500 | ret = escape_passthrough(c, &p, &osz); | 502 | escape_passthrough(c, &p, end); |
| 501 | if (ret < 0) | ||
| 502 | break; | ||
| 503 | } | 503 | } |
| 504 | 504 | ||
| 505 | *dst = p; | 505 | return p - dst; |
| 506 | |||
| 507 | if (ret < 0) | ||
| 508 | return ret; | ||
| 509 | |||
| 510 | return p - out; | ||
| 511 | } | 506 | } |
| 512 | EXPORT_SYMBOL(string_escape_mem); | 507 | EXPORT_SYMBOL(string_escape_mem); |
