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