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