summaryrefslogtreecommitdiffstats
path: root/lib/misc.c
blob: 1d20e1b09230dd4b792bc38e609d70c53776f7ff (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
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
/*
 * misc.c - various assorted functions
 *
 * Copyright (c) 2007 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
 *
 * See file CREDITS for list of people who contributed to this
 * project.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 */

#include <common.h>
#include <malloc.h>
#include <errno.h>
#include <fs.h>
#include <string.h>
#include <linux/ctype.h>

/*
 * Like simple_strtoull() but handles an optional G, M, K or k
 * suffix for Gigabyte, Megabyte or Kilobyte
 */
unsigned long long strtoull_suffix(const char *str, char **endp, int base)
{
	unsigned long long val;
	char *end;

	val = simple_strtoull(str, &end, base);

	switch (*end) {
	case 'G':
		val *= 1024;
	case 'M':
		val *= 1024;
	case 'k':
	case 'K':
		val *= 1024;
		end++;
	default:
		break;
	}

	if (strncmp(end, "iB", 2) == 0)
		end += 2;

	if (endp)
		*endp = end;

	return val;
}
EXPORT_SYMBOL(strtoull_suffix);

unsigned long strtoul_suffix(const char *str, char **endp, int base)
{
	return strtoull_suffix(str, endp, base);
}
EXPORT_SYMBOL(strtoul_suffix);

/*
 * This function parses strings in the form <startadr>[-endaddr]
 * or <startadr>[+size] and fills in start and size accordingly.
 * <startadr> and <endadr> can be given in decimal or hex (with 0x prefix)
 * and can have an optional G, M, K or k suffix.
 *
 * examples:
 * 0x1000-0x2000 -> start = 0x1000, size = 0x1001
 * 0x1000+0x1000 -> start = 0x1000, size = 0x1000
 * 0x1000        -> start = 0x1000, size = ~0
 * 1M+1k         -> start = 0x100000, size = 0x400
 */
int parse_area_spec(const char *str, loff_t *start, loff_t *size)
{
	char *endp;
	loff_t end, _start, _size;

	if (!isdigit(*str))
		return -1;

	_start = strtoull_suffix(str, &endp, 0);

	str = endp;

	if (!*str) {
		/* beginning given, but no size, assume maximum size */
		_size = ~0;
		goto success;
	}

	if (*str == '-') {
		/* beginning and end given */
		if (!isdigit(*(str + 1)))
			return -1;

		end = strtoull_suffix(str + 1, &endp, 0);
		str = endp;
		if (end < _start) {
			printf("end < start\n");
			return -1;
		}
		_size = end - _start + 1;
		goto success;
	}

	if (*str == '+') {
		/* beginning and size given */
		if (!isdigit(*(str + 1)))
			return -1;

		_size = strtoull_suffix(str + 1, &endp, 0);
		str = endp;
		goto success;
	}

	return -1;

success:
	if (*str && !isspace(*str))
		return -1;
	*start = _start;
	*size = _size;
	return 0;
}
EXPORT_SYMBOL(parse_area_spec);