8f8c4417f22865d9ffb274700540b37f1a17bd93
[firefly-linux-kernel-4.4.55.git] / lib / string_helpers.c
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/errno.h>
12 #include <linux/string.h>
13 #include <linux/string_helpers.h>
14
15 /**
16  * string_get_size - get the size in the specified units
17  * @size:       The size to be converted
18  * @units:      units to use (powers of 1000 or 1024)
19  * @buf:        buffer to format to
20  * @len:        length of buffer
21  *
22  * This function returns a string formatted to 3 significant figures
23  * giving the size in the required units.  @buf should have room for
24  * at least 9 bytes and will always be zero terminated.
25  *
26  */
27 void string_get_size(u64 size, const enum string_size_units units,
28                      char *buf, int len)
29 {
30         static const char *const units_10[] = {
31                 "B", "kB", "MB", "GB", "TB", "PB", "EB"
32         };
33         static const char *const units_2[] = {
34                 "B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"
35         };
36         static const char *const *const units_str[] = {
37                 [STRING_UNITS_10] = units_10,
38                 [STRING_UNITS_2] = units_2,
39         };
40         static const unsigned int divisor[] = {
41                 [STRING_UNITS_10] = 1000,
42                 [STRING_UNITS_2] = 1024,
43         };
44         int i, j;
45         u32 remainder = 0, sf_cap;
46         char tmp[8];
47
48         tmp[0] = '\0';
49         i = 0;
50         if (size >= divisor[units]) {
51                 while (size >= divisor[units]) {
52                         remainder = do_div(size, divisor[units]);
53                         i++;
54                 }
55
56                 sf_cap = size;
57                 for (j = 0; sf_cap*10 < 1000; j++)
58                         sf_cap *= 10;
59
60                 if (j) {
61                         remainder *= 1000;
62                         remainder /= divisor[units];
63                         snprintf(tmp, sizeof(tmp), ".%03u", remainder);
64                         tmp[j+1] = '\0';
65                 }
66         }
67
68         snprintf(buf, len, "%u%s %s", (u32)size,
69                  tmp, units_str[units][i]);
70 }
71 EXPORT_SYMBOL(string_get_size);
72
73 static bool unescape_space(char **src, char **dst)
74 {
75         char *p = *dst, *q = *src;
76
77         switch (*q) {
78         case 'n':
79                 *p = '\n';
80                 break;
81         case 'r':
82                 *p = '\r';
83                 break;
84         case 't':
85                 *p = '\t';
86                 break;
87         case 'v':
88                 *p = '\v';
89                 break;
90         case 'f':
91                 *p = '\f';
92                 break;
93         default:
94                 return false;
95         }
96         *dst += 1;
97         *src += 1;
98         return true;
99 }
100
101 static bool unescape_octal(char **src, char **dst)
102 {
103         char *p = *dst, *q = *src;
104         u8 num;
105
106         if (isodigit(*q) == 0)
107                 return false;
108
109         num = (*q++) & 7;
110         while (num < 32 && isodigit(*q) && (q - *src < 3)) {
111                 num <<= 3;
112                 num += (*q++) & 7;
113         }
114         *p = num;
115         *dst += 1;
116         *src = q;
117         return true;
118 }
119
120 static bool unescape_hex(char **src, char **dst)
121 {
122         char *p = *dst, *q = *src;
123         int digit;
124         u8 num;
125
126         if (*q++ != 'x')
127                 return false;
128
129         num = digit = hex_to_bin(*q++);
130         if (digit < 0)
131                 return false;
132
133         digit = hex_to_bin(*q);
134         if (digit >= 0) {
135                 q++;
136                 num = (num << 4) | digit;
137         }
138         *p = num;
139         *dst += 1;
140         *src = q;
141         return true;
142 }
143
144 static bool unescape_special(char **src, char **dst)
145 {
146         char *p = *dst, *q = *src;
147
148         switch (*q) {
149         case '\"':
150                 *p = '\"';
151                 break;
152         case '\\':
153                 *p = '\\';
154                 break;
155         case 'a':
156                 *p = '\a';
157                 break;
158         case 'e':
159                 *p = '\e';
160                 break;
161         default:
162                 return false;
163         }
164         *dst += 1;
165         *src += 1;
166         return true;
167 }
168
169 /**
170  * string_unescape - unquote characters in the given string
171  * @src:        source buffer (escaped)
172  * @dst:        destination buffer (unescaped)
173  * @size:       size of the destination buffer (0 to unlimit)
174  * @flags:      combination of the flags (bitwise OR):
175  *      %UNESCAPE_SPACE:
176  *              '\f' - form feed
177  *              '\n' - new line
178  *              '\r' - carriage return
179  *              '\t' - horizontal tab
180  *              '\v' - vertical tab
181  *      %UNESCAPE_OCTAL:
182  *              '\NNN' - byte with octal value NNN (1 to 3 digits)
183  *      %UNESCAPE_HEX:
184  *              '\xHH' - byte with hexadecimal value HH (1 to 2 digits)
185  *      %UNESCAPE_SPECIAL:
186  *              '\"' - double quote
187  *              '\\' - backslash
188  *              '\a' - alert (BEL)
189  *              '\e' - escape
190  *      %UNESCAPE_ANY:
191  *              all previous together
192  *
193  * Description:
194  * The function unquotes characters in the given string.
195  *
196  * Because the size of the output will be the same as or less than the size of
197  * the input, the transformation may be performed in place.
198  *
199  * Caller must provide valid source and destination pointers. Be aware that
200  * destination buffer will always be NULL-terminated. Source string must be
201  * NULL-terminated as well.
202  *
203  * Return:
204  * The amount of the characters processed to the destination buffer excluding
205  * trailing '\0' is returned.
206  */
207 int string_unescape(char *src, char *dst, size_t size, unsigned int flags)
208 {
209         char *out = dst;
210
211         while (*src && --size) {
212                 if (src[0] == '\\' && src[1] != '\0' && size > 1) {
213                         src++;
214                         size--;
215
216                         if (flags & UNESCAPE_SPACE &&
217                                         unescape_space(&src, &out))
218                                 continue;
219
220                         if (flags & UNESCAPE_OCTAL &&
221                                         unescape_octal(&src, &out))
222                                 continue;
223
224                         if (flags & UNESCAPE_HEX &&
225                                         unescape_hex(&src, &out))
226                                 continue;
227
228                         if (flags & UNESCAPE_SPECIAL &&
229                                         unescape_special(&src, &out))
230                                 continue;
231
232                         *out++ = '\\';
233                 }
234                 *out++ = *src++;
235         }
236         *out = '\0';
237
238         return out - dst;
239 }
240 EXPORT_SYMBOL(string_unescape);
241
242 static int escape_passthrough(unsigned char c, char **dst, size_t *osz)
243 {
244         char *out = *dst;
245
246         if (*osz < 1)
247                 return -ENOMEM;
248
249         *out++ = c;
250
251         *dst = out;
252         *osz -= 1;
253
254         return 1;
255 }
256
257 static int escape_space(unsigned char c, char **dst, size_t *osz)
258 {
259         char *out = *dst;
260         unsigned char to;
261
262         if (*osz < 2)
263                 return -ENOMEM;
264
265         switch (c) {
266         case '\n':
267                 to = 'n';
268                 break;
269         case '\r':
270                 to = 'r';
271                 break;
272         case '\t':
273                 to = 't';
274                 break;
275         case '\v':
276                 to = 'v';
277                 break;
278         case '\f':
279                 to = 'f';
280                 break;
281         default:
282                 return 0;
283         }
284
285         *out++ = '\\';
286         *out++ = to;
287
288         *dst = out;
289         *osz -= 2;
290
291         return 1;
292 }
293
294 static int escape_special(unsigned char c, char **dst, size_t *osz)
295 {
296         char *out = *dst;
297         unsigned char to;
298
299         if (*osz < 2)
300                 return -ENOMEM;
301
302         switch (c) {
303         case '\\':
304                 to = '\\';
305                 break;
306         case '\a':
307                 to = 'a';
308                 break;
309         case '\e':
310                 to = 'e';
311                 break;
312         default:
313                 return 0;
314         }
315
316         *out++ = '\\';
317         *out++ = to;
318
319         *dst = out;
320         *osz -= 2;
321
322         return 1;
323 }
324
325 static int escape_null(unsigned char c, char **dst, size_t *osz)
326 {
327         char *out = *dst;
328
329         if (*osz < 2)
330                 return -ENOMEM;
331
332         if (c)
333                 return 0;
334
335         *out++ = '\\';
336         *out++ = '0';
337
338         *dst = out;
339         *osz -= 2;
340
341         return 1;
342 }
343
344 static int escape_octal(unsigned char c, char **dst, size_t *osz)
345 {
346         char *out = *dst;
347
348         if (*osz < 4)
349                 return -ENOMEM;
350
351         *out++ = '\\';
352         *out++ = ((c >> 6) & 0x07) + '0';
353         *out++ = ((c >> 3) & 0x07) + '0';
354         *out++ = ((c >> 0) & 0x07) + '0';
355
356         *dst = out;
357         *osz -= 4;
358
359         return 1;
360 }
361
362 static int escape_hex(unsigned char c, char **dst, size_t *osz)
363 {
364         char *out = *dst;
365
366         if (*osz < 4)
367                 return -ENOMEM;
368
369         *out++ = '\\';
370         *out++ = 'x';
371         *out++ = hex_asc_hi(c);
372         *out++ = hex_asc_lo(c);
373
374         *dst = out;
375         *osz -= 4;
376
377         return 1;
378 }
379
380 /**
381  * string_escape_mem - quote characters in the given memory buffer
382  * @src:        source buffer (unescaped)
383  * @isz:        source buffer size
384  * @dst:        destination buffer (escaped)
385  * @osz:        destination buffer size
386  * @flags:      combination of the flags (bitwise OR):
387  *      %ESCAPE_SPACE:
388  *              '\f' - form feed
389  *              '\n' - new line
390  *              '\r' - carriage return
391  *              '\t' - horizontal tab
392  *              '\v' - vertical tab
393  *      %ESCAPE_SPECIAL:
394  *              '\\' - backslash
395  *              '\a' - alert (BEL)
396  *              '\e' - escape
397  *      %ESCAPE_NULL:
398  *              '\0' - null
399  *      %ESCAPE_OCTAL:
400  *              '\NNN' - byte with octal value NNN (3 digits)
401  *      %ESCAPE_ANY:
402  *              all previous together
403  *      %ESCAPE_NP:
404  *              escape only non-printable characters (checked by isprint)
405  *      %ESCAPE_ANY_NP:
406  *              all previous together
407  *      %ESCAPE_HEX:
408  *              '\xHH' - byte with hexadecimal value HH (2 digits)
409  * @esc:        NULL-terminated string of characters any of which, if found in
410  *              the source, has to be escaped
411  *
412  * Description:
413  * The process of escaping byte buffer includes several parts. They are applied
414  * in the following sequence.
415  *      1. The character is matched to the printable class, if asked, and in
416  *         case of match it passes through to the output.
417  *      2. The character is not matched to the one from @esc string and thus
418  *         must go as is to the output.
419  *      3. The character is checked if it falls into the class given by @flags.
420  *         %ESCAPE_OCTAL and %ESCAPE_HEX are going last since they cover any
421  *         character. Note that they actually can't go together, otherwise
422  *         %ESCAPE_HEX will be ignored.
423  *
424  * Caller must provide valid source and destination pointers. Be aware that
425  * destination buffer will not be NULL-terminated, thus caller have to append
426  * it if needs.
427  *
428  * Return:
429  * The amount of the characters processed to the destination buffer, or
430  * %-ENOMEM if the size of buffer is not enough to put an escaped character is
431  * returned.
432  *
433  * Even in the case of error @dst pointer will be updated to point to the byte
434  * after the last processed character.
435  */
436 int string_escape_mem(const char *src, size_t isz, char **dst, size_t osz,
437                       unsigned int flags, const char *esc)
438 {
439         char *out = *dst, *p = out;
440         bool is_dict = esc && *esc;
441         int ret = 0;
442
443         while (isz--) {
444                 unsigned char c = *src++;
445
446                 /*
447                  * Apply rules in the following sequence:
448                  *      - the character is printable, when @flags has
449                  *        %ESCAPE_NP bit set
450                  *      - the @esc string is supplied and does not contain a
451                  *        character under question
452                  *      - the character doesn't fall into a class of symbols
453                  *        defined by given @flags
454                  * In these cases we just pass through a character to the
455                  * output buffer.
456                  */
457                 if ((flags & ESCAPE_NP && isprint(c)) ||
458                     (is_dict && !strchr(esc, c))) {
459                         /* do nothing */
460                 } else {
461                         if (flags & ESCAPE_SPACE) {
462                                 ret = escape_space(c, &p, &osz);
463                                 if (ret < 0)
464                                         break;
465                                 if (ret > 0)
466                                         continue;
467                         }
468
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
485                         /* ESCAPE_OCTAL and ESCAPE_HEX always go last */
486                         if (flags & ESCAPE_OCTAL) {
487                                 ret = escape_octal(c, &p, &osz);
488                                 if (ret < 0)
489                                         break;
490                                 continue;
491                         }
492                         if (flags & ESCAPE_HEX) {
493                                 ret = escape_hex(c, &p, &osz);
494                                 if (ret < 0)
495                                         break;
496                                 continue;
497                         }
498                 }
499
500                 ret = escape_passthrough(c, &p, &osz);
501                 if (ret < 0)
502                         break;
503         }
504
505         *dst = p;
506
507         if (ret < 0)
508                 return ret;
509
510         return p - out;
511 }
512 EXPORT_SYMBOL(string_escape_mem);