1
/*
2
* \brief Argument list string handling
3
* \author Norman Feske
4
* \date 2006-05-22
5
*
6
* Each argument has the form:
7
*
8
* ! <key>=<value>
9
*
10
* Key is an identifier that begins with a letter or underline
11
* and may also contain digits.
12
*
13
* A list of arguments is specified by using comma as separator,
14
* whereas the first argument is considered as the weakest. If
15
* we replace an argument value of an existing argument, the
16
* existing argument is removed and we append a new argument at
17
* the end of the string.
18
*/
19
20
/*
21
* Copyright (C) 2006-2013 Genode Labs GmbH
22
*
23
* This file is part of the Genode OS framework, which is distributed
24
* under the terms of the GNU General Public License version 2.
25
*/
26
27
#
ifndef _INCLUDE__UTIL__ARG_STRING_H_
28
#
define _INCLUDE__UTIL__ARG_STRING_H_
29
30
#
include <util/token.h>
31
#
include <util/string.h>
32
#
include <base/snprintf.h>
33
34
namespace
Genode {
35
36
class
Arg_string;
37
class
Arg;
38
}
39
40
41
class
Genode::
Arg
42
{
43
/**
44
* Define tokenizer used for argument-string parsing
45
*
46
* Argument-string tokens accept C-style identifiers.
47
*/
48
typedef
::
Genode::
Token<
Scanner_policy_identifier_with_underline
>
Token
;
49
50
friend
class
Arg_string;
51
52
private
:
53
54
Token _key;
55
Token _value;
56
57
/**
58
* Return long value of argument
59
*
60
* \param out_value argument converted to unsigned long value
61
* \param out_sign 1 if positive; -1 if negative
62
* \return true if no syntactic anomaly occured
63
*
64
* This method handles the numberic modifiers G (2^30),
65
* M (2^20), and K (2^10).
66
*/
67
bool
read_ulong(
unsigned
long *
out_value
,
int *
out_sign
)
const
68
{
69
Token t =
_value;
70
71
/* check for sign; default is positive */
72
*
out_sign =
1;
73
if
(
t[0] ==
`+`
)
74
t =
t.
next(
)
;
75
else
if
(
t[0] ==
`-`
)
{
76
*
out_sign =
-
1;
77
t =
t.
next(
)
;
78
}
79
80
/* stop if token after sign is no number */
81
if
(
t.
type(
)
!=
Token::
NUMBER)
82
return
false
;
83
84
/* read numeric value and skip the corresponding tokens */
85
Number_of_bytes value;
86
size_t n =
ascii_to(
t.
start(
)
,
value)
;
87
88
if
(
n ==
0)
89
return
false
;
90
91
t =
Token(
t.
start(
)
+
n)
;
92
*
out_value =
value;
93
94
/* check for strange characters at the end of the number */
95
t =
t.
eat_whitespace(
)
;
96
if
(
t &&
(
t[0] !=
`,`
)
)
return
false
;
97
98
return
true
;
99
}
100
101
public
:
102
103
/**
104
* Construct argument from Token(s)
105
*/
106
Arg(
Token
t
=
Token(
)
)
:
_key(
t)
,
_value(
0)
107
{
108
for
(
; t &&
(
t[0] !=
`,`
)
; t =
t.
next(
)
.
eat_whitespace(
)
)
109
if
(
t[0] ==
`=`
)
{
110
_value =
t.
next(
)
.
eat_whitespace(
)
;
111
break;
112
}
113
}
114
115
inline
bool
valid(
)
const
{
return
_key
;
}
116
117
unsigned
long
ulong_value(
unsigned
long
default_value
)
const
118
{
119
unsigned
long value =
0;
120
int sign =
1;
121
122
bool valid =
read_ulong(
&
value,
&
sign)
;
123
if
(
sign <
0)
124
return
default_value
;
125
126
return
valid ?
value : default_value
;
127
}
128
129
long
long_value(
long
default_value
)
const
130
{
131
unsigned
long value =
0;
132
int sign =
1;
133
134
bool valid =
read_ulong(
&
value,
&
sign)
;
135
136
/* FIXME we should check for overflows here! */
137
return
valid ?
sign*
value : default_value
;
138
}
139
140
bool
bool_value(
bool
default_value
)
const
141
{
142
bool result =
default_value;
143
switch
(
_value.
type(
)
)
{
144
145
/* result is passed to `ascii_to` by reference */
146
case
Token::
IDENT:
;
147
if
(
ascii_to(
_value.
start(
)
,
result)
==
_value.
len(
)
)
148
return
result
;
149
150
case
Token::
STRING:
151
if
(
ascii_to(
_value.
start(
)
+
1,
result)
==
_value.
len(
)
-
2)
152
return
result
;
153
154
default
:
155
/* read values 0 (false) / !0 (true) */
156
unsigned
long value;
157
int sign;
158
bool valid =
read_ulong(
&
value,
&
sign)
;
159
160
return
valid ?
value : default_value
;
161
}
162
}
163
164
void
key(
char *
dst
,
size_t
dst_len
)
const
165
{
166
_key.
string(
dst,
dst_len)
;
167
}
168
169
void
string(
char *
dst
,
size_t
dst_len
,
const
char *
default_string
)
const
170
{
171
/* check for one-word string w/o quotes */
172
if
(
_value.
type(
)
==
Token::
IDENT)
{
173
size_t len =
min(
dst_len -
1,
_value.
len(
)
)
;
174
memcpy(
dst,
_value.
start(
)
,
len)
;
175
dst[len]
=
0;
176
return
;
177
}
178
179
/* stop here if _value is not a string */
180
if
(
_value.
type(
)
!=
Token::
STRING)
{
181
strncpy(
dst,
default_string,
dst_len)
;
182
return
;
183
}
184
185
/* unpack string to dst */
186
size_t num_chars =
min(
dst_len -
1,
_value.
len(
)
)
;
187
unpack_string(
_value.
start(
)
,
dst,
num_chars)
;
188
}
189
}
;
190
191
192
class
Genode::
Arg_string
193
{
194
typedef
Arg::
Token Token
;
195
196
private
:
197
198
static
Token
_next_key(
Token
t
)
199
{
200
for
(
; t; t =
t.
next(
)
.
eat_whitespace(
)
)
201
202
/* if we find a comma, return token after comma */
203
if
(
t[0] ==
`,`
)
return
t.
next(
)
.
eat_whitespace(
)
;
204
205
return
Token(
)
;
206
}
207
208
/**
209
* Find key token in argument string
210
*/
211
static
Token
_find_key(
const
char *
args
,
const
char *
key
)
212
{
213
for
(
Token t(
args)
; t; t =
_next_key(
t)
)
214
215
/* check if key matches */
216
if
(
(
t.
type(
)
==
Token::
IDENT)
&&
!
strcmp(
key,
t.
start(
)
,
t.
len(
)
)
)
217
return
t
;
218
219
return
Token(
)
;
220
}
221
222
/**
223
* Append source string to destination string
224
*
225
* NOTE: check string length before calling this method!
226
*
227
* \return last character of result string
228
*/
229
static
char *
_append(
char *
dst
,
const
char *
src
)
230
{
231
unsigned
src_len =
strlen(
src)
;
232
while
(
*
dst)
dst++
;
233
memcpy(
dst,
src,
src_len +
1)
;
234
return
dst +
src_len
;
235
}
236
237
public
:
238
239
/**
240
* Find argument by its key
241
*/
242
static
Arg
find_arg(
const
char *
args
,
const
char *
key
)
{
243
return
(
args &&
key)
?
Arg(
_find_key(
args,
key)
)
: Arg(
)
;
}
244
245
static
Arg
first_arg(
const
char *
args
)
{
246
return
Arg(
Token(
args)
)
;
}
247
248
/**
249
* Remove argument with the specified key
250
*/
251
static
bool
remove_arg(
char *
args
,
const
char *
key
)
252
{
253
if
(
!
args ||
!
key)
return
false
;
254
255
Token beg =
_find_key(
args,
key)
;
256
Token next =
_next_key(
beg)
;
257
258
/* no such key to remove - we are done */
259
if
(
!
beg)
return
true
;
260
261
/* if argument is the last one, null-terminate string right here */
262
if
(
!
next)
{
263
264
/* eat all pending whitespaces at the end of the string */
265
char *
s =
max(
beg.
start(
)
-
1,
args)
;
266
while
(
s >
args &&
(
*
s ==
` `
)
)
s--
;
267
268
/* write string-terminating zero */
269
*
s =
0;
270
}
else
271
memcpy(
beg.
start(
)
,
next.
start(
)
,
strlen(
next.
start(
)
)
+
1)
;
272
273
return
true
;
274
}
275
276
/**
277
* Add new argument
278
*/
279
static
bool
add_arg(
char *
args
,
unsigned
args_len
,
280
const
char *
key
,
const
char *
value
,
281
Token::
Type type =
Token::
Type::
IDENT
)
282
{
283
if
(
!
args ||
!
key ||
!
value)
return
false
;
284
285
unsigned
old_len =
strlen(
args)
;
286
287
/* check if args string has enough capacity */
288
if
(
(
type ==
Token::
Type::
STRING
289
&&
old_len +
strlen(
key)
+
strlen(
value)
+
4 >
args_len)
290
||
(
old_len +
strlen(
key)
+
strlen(
value)
+
2 >
args_len)
)
291
return
false
;
292
293
args +=
old_len;
294
295
if
(
old_len)
296
args =
_append(
args,
", "
)
;
297
298
if
(
type ==
Token::
Type::
STRING)
299
_append(_append(_append(
_append(
args,
key)
,
"=\"
"), value), "
\""
)
;
300
else
301
_append(
_append(
_append(
args,
key)
,
"="
)
,
value)
;
302
return
true
;
303
}
304
305
/**
306
* Assign new value to argument
307
*/
308
static
bool
set_arg(
char *
args
,
unsigned
args_len
,
309
const
char *
key
,
const
char *
value
)
310
{
311
return
remove_arg(
args,
key)
&&
add_arg(
args,
args_len,
key,
value)
;
312
}
313
314
/**
315
* Assign new integer argument
316
*/
317
static
bool
set_arg(
char *
args
,
unsigned
args_len
,
318
const
char *
key
,
int
value
)
319
{
320
enum
{
STRING_LONG_MAX =
32
}
;
321
char buf[STRING_LONG_MAX]
;
322
snprintf(
buf,
sizeof
(
buf)
,
"%d"
,
value)
;
323
return
remove_arg(
args,
key)
&&
add_arg(
args,
args_len,
key,
buf)
;
324
}
325
326
/**
327
* Assign new string argument
328
*/
329
static
bool
set_arg_string(
char *
args
,
unsigned
args_len
,
330
const
char *
key
,
const
char *
value
)
331
{
332
return
remove_arg(
args,
key)
333
&&
add_arg(
args,
args_len,
key,
value,
Token::
Type::
STRING)
;
334
}
335
}
;
336
337
#
endif /* _INCLUDE__UTIL__ARG_STRING_H_ */