1
2
3
4
5
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/errno.h>
13#include <linux/string.h>
14#include <linux/string_helpers.h>
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29void string_get_size(u64 size, u64 blk_size, const enum string_size_units units,
30 char *buf, int len)
31{
32 static const char *const units_10[] = {
33 "B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"
34 };
35 static const char *const units_2[] = {
36 "B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"
37 };
38 static const char *const *const units_str[] = {
39 [STRING_UNITS_10] = units_10,
40 [STRING_UNITS_2] = units_2,
41 };
42 static const unsigned int divisor[] = {
43 [STRING_UNITS_10] = 1000,
44 [STRING_UNITS_2] = 1024,
45 };
46 static const unsigned int rounding[] = { 500, 50, 5 };
47 int i = 0, j;
48 u32 remainder = 0, sf_cap;
49 char tmp[8];
50 const char *unit;
51
52 tmp[0] = '\0';
53
54 if (blk_size == 0)
55 size = 0;
56 if (size == 0)
57 goto out;
58
59
60
61
62
63
64
65
66
67
68
69
70 while (blk_size >> 32) {
71 do_div(blk_size, divisor[units]);
72 i++;
73 }
74
75 while (size >> 32) {
76 do_div(size, divisor[units]);
77 i++;
78 }
79
80
81
82 size *= blk_size;
83
84
85 while (size >= divisor[units]) {
86 remainder = do_div(size, divisor[units]);
87 i++;
88 }
89
90
91
92 sf_cap = size;
93 for (j = 0; sf_cap*10 < 1000; j++)
94 sf_cap *= 10;
95
96 if (units == STRING_UNITS_2) {
97
98
99
100 remainder *= 1000;
101 remainder >>= 10;
102 }
103
104
105
106 remainder += rounding[j];
107 if (remainder >= 1000) {
108 remainder -= 1000;
109 size += 1;
110 }
111
112 if (j) {
113 snprintf(tmp, sizeof(tmp), ".%03u", remainder);
114 tmp[j+1] = '\0';
115 }
116
117 out:
118 if (i >= ARRAY_SIZE(units_2))
119 unit = "UNK";
120 else
121 unit = units_str[units][i];
122
123 snprintf(buf, len, "%u%s %s", (u32)size,
124 tmp, unit);
125}
126EXPORT_SYMBOL(string_get_size);
127
128static bool unescape_space(char **src, char **dst)
129{
130 char *p = *dst, *q = *src;
131
132 switch (*q) {
133 case 'n':
134 *p = '\n';
135 break;
136 case 'r':
137 *p = '\r';
138 break;
139 case 't':
140 *p = '\t';
141 break;
142 case 'v':
143 *p = '\v';
144 break;
145 case 'f':
146 *p = '\f';
147 break;
148 default:
149 return false;
150 }
151 *dst += 1;
152 *src += 1;
153 return true;
154}
155
156static bool unescape_octal(char **src, char **dst)
157{
158 char *p = *dst, *q = *src;
159 u8 num;
160
161 if (isodigit(*q) == 0)
162 return false;
163
164 num = (*q++) & 7;
165 while (num < 32 && isodigit(*q) && (q - *src < 3)) {
166 num <<= 3;
167 num += (*q++) & 7;
168 }
169 *p = num;
170 *dst += 1;
171 *src = q;
172 return true;
173}
174
175static bool unescape_hex(char **src, char **dst)
176{
177 char *p = *dst, *q = *src;
178 int digit;
179 u8 num;
180
181 if (*q++ != 'x')
182 return false;
183
184 num = digit = hex_to_bin(*q++);
185 if (digit < 0)
186 return false;
187
188 digit = hex_to_bin(*q);
189 if (digit >= 0) {
190 q++;
191 num = (num << 4) | digit;
192 }
193 *p = num;
194 *dst += 1;
195 *src = q;
196 return true;
197}
198
199static bool unescape_special(char **src, char **dst)
200{
201 char *p = *dst, *q = *src;
202
203 switch (*q) {
204 case '\"':
205 *p = '\"';
206 break;
207 case '\\':
208 *p = '\\';
209 break;
210 case 'a':
211 *p = '\a';
212 break;
213 case 'e':
214 *p = '\e';
215 break;
216 default:
217 return false;
218 }
219 *dst += 1;
220 *src += 1;
221 return true;
222}
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262int string_unescape(char *src, char *dst, size_t size, unsigned int flags)
263{
264 char *out = dst;
265
266 while (*src && --size) {
267 if (src[0] == '\\' && src[1] != '\0' && size > 1) {
268 src++;
269 size--;
270
271 if (flags & UNESCAPE_SPACE &&
272 unescape_space(&src, &out))
273 continue;
274
275 if (flags & UNESCAPE_OCTAL &&
276 unescape_octal(&src, &out))
277 continue;
278
279 if (flags & UNESCAPE_HEX &&
280 unescape_hex(&src, &out))
281 continue;
282
283 if (flags & UNESCAPE_SPECIAL &&
284 unescape_special(&src, &out))
285 continue;
286
287 *out++ = '\\';
288 }
289 *out++ = *src++;
290 }
291 *out = '\0';
292
293 return out - dst;
294}
295EXPORT_SYMBOL(string_unescape);
296
297static bool escape_passthrough(unsigned char c, char **dst, char *end)
298{
299 char *out = *dst;
300
301 if (out < end)
302 *out = c;
303 *dst = out + 1;
304 return true;
305}
306
307static bool escape_space(unsigned char c, char **dst, char *end)
308{
309 char *out = *dst;
310 unsigned char to;
311
312 switch (c) {
313 case '\n':
314 to = 'n';
315 break;
316 case '\r':
317 to = 'r';
318 break;
319 case '\t':
320 to = 't';
321 break;
322 case '\v':
323 to = 'v';
324 break;
325 case '\f':
326 to = 'f';
327 break;
328 default:
329 return false;
330 }
331
332 if (out < end)
333 *out = '\\';
334 ++out;
335 if (out < end)
336 *out = to;
337 ++out;
338
339 *dst = out;
340 return true;
341}
342
343static bool escape_special(unsigned char c, char **dst, char *end)
344{
345 char *out = *dst;
346 unsigned char to;
347
348 switch (c) {
349 case '\\':
350 to = '\\';
351 break;
352 case '\a':
353 to = 'a';
354 break;
355 case '\e':
356 to = 'e';
357 break;
358 default:
359 return false;
360 }
361
362 if (out < end)
363 *out = '\\';
364 ++out;
365 if (out < end)
366 *out = to;
367 ++out;
368
369 *dst = out;
370 return true;
371}
372
373static bool escape_null(unsigned char c, char **dst, char *end)
374{
375 char *out = *dst;
376
377 if (c)
378 return false;
379
380 if (out < end)
381 *out = '\\';
382 ++out;
383 if (out < end)
384 *out = '0';
385 ++out;
386
387 *dst = out;
388 return true;
389}
390
391static bool escape_octal(unsigned char c, char **dst, char *end)
392{
393 char *out = *dst;
394
395 if (out < end)
396 *out = '\\';
397 ++out;
398 if (out < end)
399 *out = ((c >> 6) & 0x07) + '0';
400 ++out;
401 if (out < end)
402 *out = ((c >> 3) & 0x07) + '0';
403 ++out;
404 if (out < end)
405 *out = ((c >> 0) & 0x07) + '0';
406 ++out;
407
408 *dst = out;
409 return true;
410}
411
412static bool escape_hex(unsigned char c, char **dst, char *end)
413{
414 char *out = *dst;
415
416 if (out < end)
417 *out = '\\';
418 ++out;
419 if (out < end)
420 *out = 'x';
421 ++out;
422 if (out < end)
423 *out = hex_asc_hi(c);
424 ++out;
425 if (out < end)
426 *out = hex_asc_lo(c);
427 ++out;
428
429 *dst = out;
430 return true;
431}
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489int string_escape_mem(const char *src, size_t isz, char *dst, size_t osz,
490 unsigned int flags, const char *only)
491{
492 char *p = dst;
493 char *end = p + osz;
494 bool is_dict = only && *only;
495
496 while (isz--) {
497 unsigned char c = *src++;
498
499
500
501
502
503
504
505
506
507
508
509
510 if ((flags & ESCAPE_NP && isprint(c)) ||
511 (is_dict && !strchr(only, c))) {
512
513 } else {
514 if (flags & ESCAPE_SPACE && escape_space(c, &p, end))
515 continue;
516
517 if (flags & ESCAPE_SPECIAL && escape_special(c, &p, end))
518 continue;
519
520 if (flags & ESCAPE_NULL && escape_null(c, &p, end))
521 continue;
522
523
524 if (flags & ESCAPE_OCTAL && escape_octal(c, &p, end))
525 continue;
526
527 if (flags & ESCAPE_HEX && escape_hex(c, &p, end))
528 continue;
529 }
530
531 escape_passthrough(c, &p, end);
532 }
533
534 return p - dst;
535}
536EXPORT_SYMBOL(string_escape_mem);
537