Blame view

lib/string_helpers.c 10.2 KB
3c9f3681d   James Bottomley   [SCSI] lib: add g...
1
2
3
4
  /*
   * Helpers for formatting and printing strings
   *
   * Copyright 31 August 2008 James Bottomley
16c7fa058   Andy Shevchenko   lib/string_helper...
5
   * Copyright (C) 2013, Intel Corporation
3c9f3681d   James Bottomley   [SCSI] lib: add g...
6
   */
b9f28d863   James Bottomley   sd, mmc, virtio_b...
7
  #include <linux/bug.h>
3c9f3681d   James Bottomley   [SCSI] lib: add g...
8
9
  #include <linux/kernel.h>
  #include <linux/math64.h>
8bc3bcc93   Paul Gortmaker   lib: reduce the u...
10
  #include <linux/export.h>
16c7fa058   Andy Shevchenko   lib/string_helper...
11
  #include <linux/ctype.h>
c8250381c   Andy Shevchenko   lib / string_help...
12
13
  #include <linux/errno.h>
  #include <linux/string.h>
3c9f3681d   James Bottomley   [SCSI] lib: add g...
14
15
16
17
  #include <linux/string_helpers.h>
  
  /**
   * string_get_size - get the size in the specified units
b9f28d863   James Bottomley   sd, mmc, virtio_b...
18
19
   * @size:	The size to be converted in blocks
   * @blk_size:	Size of the block (use 1 for size in bytes)
3c9f3681d   James Bottomley   [SCSI] lib: add g...
20
21
22
23
24
   * @units:	units to use (powers of 1000 or 1024)
   * @buf:	buffer to format to
   * @len:	length of buffer
   *
   * This function returns a string formatted to 3 significant figures
d1214c65c   Rasmus Villemoes   libstring_helpers...
25
26
   * giving the size in the required units.  @buf should have room for
   * at least 9 bytes and will always be zero terminated.
3c9f3681d   James Bottomley   [SCSI] lib: add g...
27
28
   *
   */
b9f28d863   James Bottomley   sd, mmc, virtio_b...
29
  void string_get_size(u64 size, u64 blk_size, const enum string_size_units units,
d1214c65c   Rasmus Villemoes   libstring_helpers...
30
  		     char *buf, int len)
3c9f3681d   James Bottomley   [SCSI] lib: add g...
31
  {
142cda5db   Mathias Krause   lib/string_helper...
32
  	static const char *const units_10[] = {
b9f28d863   James Bottomley   sd, mmc, virtio_b...
33
  		"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"
142cda5db   Mathias Krause   lib/string_helper...
34
35
  	};
  	static const char *const units_2[] = {
b9f28d863   James Bottomley   sd, mmc, virtio_b...
36
  		"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"
142cda5db   Mathias Krause   lib/string_helper...
37
38
39
  	};
  	static const char *const *const units_str[] = {
  		[STRING_UNITS_10] = units_10,
3c9f3681d   James Bottomley   [SCSI] lib: add g...
40
41
  		[STRING_UNITS_2] = units_2,
  	};
68aecfb97   Andrew Morton   lib/string_helper...
42
  	static const unsigned int divisor[] = {
3c9f3681d   James Bottomley   [SCSI] lib: add g...
43
44
45
46
  		[STRING_UNITS_10] = 1000,
  		[STRING_UNITS_2] = 1024,
  	};
  	int i, j;
b9f28d863   James Bottomley   sd, mmc, virtio_b...
47
  	u32 remainder = 0, sf_cap, exp;
3c9f3681d   James Bottomley   [SCSI] lib: add g...
48
  	char tmp[8];
b9f28d863   James Bottomley   sd, mmc, virtio_b...
49
  	const char *unit;
3c9f3681d   James Bottomley   [SCSI] lib: add g...
50
51
  
  	tmp[0] = '\0';
a8659597b   H. Peter Anvin   [SCSI] lib: strin...
52
  	i = 0;
b9f28d863   James Bottomley   sd, mmc, virtio_b...
53
54
  	if (!size)
  		goto out;
3c9f3681d   James Bottomley   [SCSI] lib: add g...
55

b9f28d863   James Bottomley   sd, mmc, virtio_b...
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
  	while (blk_size >= divisor[units]) {
  		remainder = do_div(blk_size, divisor[units]);
  		i++;
  	}
  
  	exp = divisor[units] / (u32)blk_size;
  	if (size >= exp) {
  		remainder = do_div(size, divisor[units]);
  		remainder *= blk_size;
  		i++;
  	} else {
  		remainder *= size;
  	}
  
  	size *= blk_size;
  	size += remainder / divisor[units];
  	remainder %= divisor[units];
3c9f3681d   James Bottomley   [SCSI] lib: add g...
73

b9f28d863   James Bottomley   sd, mmc, virtio_b...
74
75
76
  	while (size >= divisor[units]) {
  		remainder = do_div(size, divisor[units]);
  		i++;
3c9f3681d   James Bottomley   [SCSI] lib: add g...
77
  	}
b9f28d863   James Bottomley   sd, mmc, virtio_b...
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
  	sf_cap = size;
  	for (j = 0; sf_cap*10 < 1000; j++)
  		sf_cap *= 10;
  
  	if (j) {
  		remainder *= 1000;
  		remainder /= divisor[units];
  		snprintf(tmp, sizeof(tmp), ".%03u", remainder);
  		tmp[j+1] = '\0';
  	}
  
   out:
  	if (i >= ARRAY_SIZE(units_2))
  		unit = "UNK";
  	else
  		unit = units_str[units][i];
84b9fbedf   Rasmus Villemoes   lib/string_helper...
94
  	snprintf(buf, len, "%u%s %s", (u32)size,
b9f28d863   James Bottomley   sd, mmc, virtio_b...
95
  		 tmp, unit);
3c9f3681d   James Bottomley   [SCSI] lib: add g...
96
97
  }
  EXPORT_SYMBOL(string_get_size);
16c7fa058   Andy Shevchenko   lib/string_helper...
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
  
  static bool unescape_space(char **src, char **dst)
  {
  	char *p = *dst, *q = *src;
  
  	switch (*q) {
  	case 'n':
  		*p = '
  ';
  		break;
  	case 'r':
  		*p = '\r';
  		break;
  	case 't':
  		*p = '\t';
  		break;
  	case 'v':
  		*p = '\v';
  		break;
  	case 'f':
  		*p = '\f';
  		break;
  	default:
  		return false;
  	}
  	*dst += 1;
  	*src += 1;
  	return true;
  }
  
  static bool unescape_octal(char **src, char **dst)
  {
  	char *p = *dst, *q = *src;
  	u8 num;
  
  	if (isodigit(*q) == 0)
  		return false;
  
  	num = (*q++) & 7;
  	while (num < 32 && isodigit(*q) && (q - *src < 3)) {
  		num <<= 3;
  		num += (*q++) & 7;
  	}
  	*p = num;
  	*dst += 1;
  	*src = q;
  	return true;
  }
  
  static bool unescape_hex(char **src, char **dst)
  {
  	char *p = *dst, *q = *src;
  	int digit;
  	u8 num;
  
  	if (*q++ != 'x')
  		return false;
  
  	num = digit = hex_to_bin(*q++);
  	if (digit < 0)
  		return false;
  
  	digit = hex_to_bin(*q);
  	if (digit >= 0) {
  		q++;
  		num = (num << 4) | digit;
  	}
  	*p = num;
  	*dst += 1;
  	*src = q;
  	return true;
  }
  
  static bool unescape_special(char **src, char **dst)
  {
  	char *p = *dst, *q = *src;
  
  	switch (*q) {
  	case '\"':
  		*p = '\"';
  		break;
  	case '\\':
  		*p = '\\';
  		break;
  	case 'a':
  		*p = '\a';
  		break;
  	case 'e':
  		*p = '\e';
  		break;
  	default:
  		return false;
  	}
  	*dst += 1;
  	*src += 1;
  	return true;
  }
d295634e9   Andy Shevchenko   lib / string_help...
195
196
197
198
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
  /**
   * string_unescape - unquote characters in the given string
   * @src:	source buffer (escaped)
   * @dst:	destination buffer (unescaped)
   * @size:	size of the destination buffer (0 to unlimit)
   * @flags:	combination of the flags (bitwise OR):
   *	%UNESCAPE_SPACE:
   *		'\f' - form feed
   *		'
  ' - new line
   *		'\r' - carriage return
   *		'\t' - horizontal tab
   *		'\v' - vertical tab
   *	%UNESCAPE_OCTAL:
   *		'\NNN' - byte with octal value NNN (1 to 3 digits)
   *	%UNESCAPE_HEX:
   *		'\xHH' - byte with hexadecimal value HH (1 to 2 digits)
   *	%UNESCAPE_SPECIAL:
   *		'\"' - double quote
   *		'\\' - backslash
   *		'\a' - alert (BEL)
   *		'\e' - escape
   *	%UNESCAPE_ANY:
   *		all previous together
   *
   * Description:
   * The function unquotes characters in the given string.
   *
   * Because the size of the output will be the same as or less than the size of
   * the input, the transformation may be performed in place.
   *
   * Caller must provide valid source and destination pointers. Be aware that
   * destination buffer will always be NULL-terminated. Source string must be
   * NULL-terminated as well.
   *
   * Return:
   * The amount of the characters processed to the destination buffer excluding
   * trailing '\0' is returned.
   */
16c7fa058   Andy Shevchenko   lib/string_helper...
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
262
263
264
265
266
267
  int string_unescape(char *src, char *dst, size_t size, unsigned int flags)
  {
  	char *out = dst;
  
  	while (*src && --size) {
  		if (src[0] == '\\' && src[1] != '\0' && size > 1) {
  			src++;
  			size--;
  
  			if (flags & UNESCAPE_SPACE &&
  					unescape_space(&src, &out))
  				continue;
  
  			if (flags & UNESCAPE_OCTAL &&
  					unescape_octal(&src, &out))
  				continue;
  
  			if (flags & UNESCAPE_HEX &&
  					unescape_hex(&src, &out))
  				continue;
  
  			if (flags & UNESCAPE_SPECIAL &&
  					unescape_special(&src, &out))
  				continue;
  
  			*out++ = '\\';
  		}
  		*out++ = *src++;
  	}
  	*out = '\0';
  
  	return out - dst;
  }
  EXPORT_SYMBOL(string_unescape);
c8250381c   Andy Shevchenko   lib / string_help...
268

3aeddc7d6   Rasmus Villemoes   lib/string_helper...
269
  static bool escape_passthrough(unsigned char c, char **dst, char *end)
c8250381c   Andy Shevchenko   lib / string_help...
270
271
  {
  	char *out = *dst;
3aeddc7d6   Rasmus Villemoes   lib/string_helper...
272
273
274
275
  	if (out < end)
  		*out = c;
  	*dst = out + 1;
  	return true;
c8250381c   Andy Shevchenko   lib / string_help...
276
  }
3aeddc7d6   Rasmus Villemoes   lib/string_helper...
277
  static bool escape_space(unsigned char c, char **dst, char *end)
c8250381c   Andy Shevchenko   lib / string_help...
278
279
280
  {
  	char *out = *dst;
  	unsigned char to;
c8250381c   Andy Shevchenko   lib / string_help...
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
  	switch (c) {
  	case '
  ':
  		to = 'n';
  		break;
  	case '\r':
  		to = 'r';
  		break;
  	case '\t':
  		to = 't';
  		break;
  	case '\v':
  		to = 'v';
  		break;
  	case '\f':
  		to = 'f';
  		break;
  	default:
3aeddc7d6   Rasmus Villemoes   lib/string_helper...
299
  		return false;
c8250381c   Andy Shevchenko   lib / string_help...
300
  	}
3aeddc7d6   Rasmus Villemoes   lib/string_helper...
301
302
303
304
305
306
  	if (out < end)
  		*out = '\\';
  	++out;
  	if (out < end)
  		*out = to;
  	++out;
c8250381c   Andy Shevchenko   lib / string_help...
307

3aeddc7d6   Rasmus Villemoes   lib/string_helper...
308
309
  	*dst = out;
  	return true;
c8250381c   Andy Shevchenko   lib / string_help...
310
  }
3aeddc7d6   Rasmus Villemoes   lib/string_helper...
311
  static bool escape_special(unsigned char c, char **dst, char *end)
c8250381c   Andy Shevchenko   lib / string_help...
312
313
314
  {
  	char *out = *dst;
  	unsigned char to;
c8250381c   Andy Shevchenko   lib / string_help...
315
316
317
318
319
320
321
322
323
324
325
  	switch (c) {
  	case '\\':
  		to = '\\';
  		break;
  	case '\a':
  		to = 'a';
  		break;
  	case '\e':
  		to = 'e';
  		break;
  	default:
3aeddc7d6   Rasmus Villemoes   lib/string_helper...
326
  		return false;
c8250381c   Andy Shevchenko   lib / string_help...
327
  	}
3aeddc7d6   Rasmus Villemoes   lib/string_helper...
328
329
330
331
332
333
  	if (out < end)
  		*out = '\\';
  	++out;
  	if (out < end)
  		*out = to;
  	++out;
c8250381c   Andy Shevchenko   lib / string_help...
334

3aeddc7d6   Rasmus Villemoes   lib/string_helper...
335
336
  	*dst = out;
  	return true;
c8250381c   Andy Shevchenko   lib / string_help...
337
  }
3aeddc7d6   Rasmus Villemoes   lib/string_helper...
338
  static bool escape_null(unsigned char c, char **dst, char *end)
c8250381c   Andy Shevchenko   lib / string_help...
339
340
  {
  	char *out = *dst;
c8250381c   Andy Shevchenko   lib / string_help...
341
  	if (c)
3aeddc7d6   Rasmus Villemoes   lib/string_helper...
342
  		return false;
c8250381c   Andy Shevchenko   lib / string_help...
343

3aeddc7d6   Rasmus Villemoes   lib/string_helper...
344
345
346
347
348
349
  	if (out < end)
  		*out = '\\';
  	++out;
  	if (out < end)
  		*out = '0';
  	++out;
c8250381c   Andy Shevchenko   lib / string_help...
350

3aeddc7d6   Rasmus Villemoes   lib/string_helper...
351
352
  	*dst = out;
  	return true;
c8250381c   Andy Shevchenko   lib / string_help...
353
  }
3aeddc7d6   Rasmus Villemoes   lib/string_helper...
354
  static bool escape_octal(unsigned char c, char **dst, char *end)
c8250381c   Andy Shevchenko   lib / string_help...
355
356
  {
  	char *out = *dst;
3aeddc7d6   Rasmus Villemoes   lib/string_helper...
357
358
359
360
361
362
363
364
365
366
367
368
  	if (out < end)
  		*out = '\\';
  	++out;
  	if (out < end)
  		*out = ((c >> 6) & 0x07) + '0';
  	++out;
  	if (out < end)
  		*out = ((c >> 3) & 0x07) + '0';
  	++out;
  	if (out < end)
  		*out = ((c >> 0) & 0x07) + '0';
  	++out;
c8250381c   Andy Shevchenko   lib / string_help...
369
370
  
  	*dst = out;
3aeddc7d6   Rasmus Villemoes   lib/string_helper...
371
  	return true;
c8250381c   Andy Shevchenko   lib / string_help...
372
  }
3aeddc7d6   Rasmus Villemoes   lib/string_helper...
373
  static bool escape_hex(unsigned char c, char **dst, char *end)
c8250381c   Andy Shevchenko   lib / string_help...
374
375
  {
  	char *out = *dst;
3aeddc7d6   Rasmus Villemoes   lib/string_helper...
376
377
378
379
380
381
382
383
384
385
386
387
  	if (out < end)
  		*out = '\\';
  	++out;
  	if (out < end)
  		*out = 'x';
  	++out;
  	if (out < end)
  		*out = hex_asc_hi(c);
  	++out;
  	if (out < end)
  		*out = hex_asc_lo(c);
  	++out;
c8250381c   Andy Shevchenko   lib / string_help...
388
389
  
  	*dst = out;
3aeddc7d6   Rasmus Villemoes   lib/string_helper...
390
  	return true;
c8250381c   Andy Shevchenko   lib / string_help...
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
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
  }
  
  /**
   * string_escape_mem - quote characters in the given memory buffer
   * @src:	source buffer (unescaped)
   * @isz:	source buffer size
   * @dst:	destination buffer (escaped)
   * @osz:	destination buffer size
   * @flags:	combination of the flags (bitwise OR):
   *	%ESCAPE_SPACE:
   *		'\f' - form feed
   *		'
  ' - new line
   *		'\r' - carriage return
   *		'\t' - horizontal tab
   *		'\v' - vertical tab
   *	%ESCAPE_SPECIAL:
   *		'\\' - backslash
   *		'\a' - alert (BEL)
   *		'\e' - escape
   *	%ESCAPE_NULL:
   *		'\0' - null
   *	%ESCAPE_OCTAL:
   *		'\NNN' - byte with octal value NNN (3 digits)
   *	%ESCAPE_ANY:
   *		all previous together
   *	%ESCAPE_NP:
   *		escape only non-printable characters (checked by isprint)
   *	%ESCAPE_ANY_NP:
   *		all previous together
   *	%ESCAPE_HEX:
   *		'\xHH' - byte with hexadecimal value HH (2 digits)
   * @esc:	NULL-terminated string of characters any of which, if found in
   *		the source, has to be escaped
   *
   * Description:
   * The process of escaping byte buffer includes several parts. They are applied
   * in the following sequence.
   *	1. The character is matched to the printable class, if asked, and in
   *	   case of match it passes through to the output.
   *	2. The character is not matched to the one from @esc string and thus
   *	   must go as is to the output.
   *	3. The character is checked if it falls into the class given by @flags.
   *	   %ESCAPE_OCTAL and %ESCAPE_HEX are going last since they cover any
   *	   character. Note that they actually can't go together, otherwise
   *	   %ESCAPE_HEX will be ignored.
   *
   * Caller must provide valid source and destination pointers. Be aware that
   * destination buffer will not be NULL-terminated, thus caller have to append
   * it if needs.
   *
   * Return:
41416f233   Rasmus Villemoes   lib/string_helper...
443
444
445
446
   * The total size of the escaped output that would be generated for
   * the given input and flags. To check whether the output was
   * truncated, compare the return value to osz. There is room left in
   * dst for a '\0' terminator if and only if ret < osz.
c8250381c   Andy Shevchenko   lib / string_help...
447
   */
41416f233   Rasmus Villemoes   lib/string_helper...
448
  int string_escape_mem(const char *src, size_t isz, char *dst, size_t osz,
c8250381c   Andy Shevchenko   lib / string_help...
449
450
  		      unsigned int flags, const char *esc)
  {
41416f233   Rasmus Villemoes   lib/string_helper...
451
  	char *p = dst;
3aeddc7d6   Rasmus Villemoes   lib/string_helper...
452
  	char *end = p + osz;
c8250381c   Andy Shevchenko   lib / string_help...
453
  	bool is_dict = esc && *esc;
c8250381c   Andy Shevchenko   lib / string_help...
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
  
  	while (isz--) {
  		unsigned char c = *src++;
  
  		/*
  		 * Apply rules in the following sequence:
  		 *	- the character is printable, when @flags has
  		 *	  %ESCAPE_NP bit set
  		 *	- the @esc string is supplied and does not contain a
  		 *	  character under question
  		 *	- the character doesn't fall into a class of symbols
  		 *	  defined by given @flags
  		 * In these cases we just pass through a character to the
  		 * output buffer.
  		 */
  		if ((flags & ESCAPE_NP && isprint(c)) ||
  		    (is_dict && !strchr(esc, c))) {
  			/* do nothing */
  		} else {
3aeddc7d6   Rasmus Villemoes   lib/string_helper...
473
474
475
476
477
478
479
480
  			if (flags & ESCAPE_SPACE && escape_space(c, &p, end))
  				continue;
  
  			if (flags & ESCAPE_SPECIAL && escape_special(c, &p, end))
  				continue;
  
  			if (flags & ESCAPE_NULL && escape_null(c, &p, end))
  				continue;
c8250381c   Andy Shevchenko   lib / string_help...
481
482
  
  			/* ESCAPE_OCTAL and ESCAPE_HEX always go last */
3aeddc7d6   Rasmus Villemoes   lib/string_helper...
483
  			if (flags & ESCAPE_OCTAL && escape_octal(c, &p, end))
c8250381c   Andy Shevchenko   lib / string_help...
484
  				continue;
3aeddc7d6   Rasmus Villemoes   lib/string_helper...
485
486
  
  			if (flags & ESCAPE_HEX && escape_hex(c, &p, end))
c8250381c   Andy Shevchenko   lib / string_help...
487
  				continue;
c8250381c   Andy Shevchenko   lib / string_help...
488
  		}
3aeddc7d6   Rasmus Villemoes   lib/string_helper...
489
  		escape_passthrough(c, &p, end);
c8250381c   Andy Shevchenko   lib / string_help...
490
  	}
41416f233   Rasmus Villemoes   lib/string_helper...
491
  	return p - dst;
c8250381c   Andy Shevchenko   lib / string_help...
492
493
  }
  EXPORT_SYMBOL(string_escape_mem);