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
38 class Arg
39 {
40 /**
41 * Define tokenizer used for argument-string parsing
42 *
43 * Argument-string tokens accept C-style identifiers.
44 */
45 typedef ::Genode::Token<Scanner_policy_identifier_with_underline> Token;
46
47 friend class Arg_string;
48
49 private:
50
51 Token _key;
52 Token _value;
53
54 /**
55 * Return long value of argument
56 *
57 * \param out_value argument converted to unsigned long value
58 * \param out_sign 1 if positive; -1 if negative
59 * \return true if no syntactic anomaly occured
60 *
61 * This function handles the numberic modifiers G (2^30),
62 * M (2^20), and K (2^10).
63 */
64 bool read_ulong(unsigned long *out_value, int *out_sign) const
65 {
66 Token t = _value;
67
68 /* check for sign; default is positive */
69 *out_sign = 1;
70 if (t[0] == `+`)
71 t = t.next();
72 else if (t[0] == `-`) {
73 *out_sign = -1;
74 t = t.next();
75 }
76
77 /* stop if token after sign is no number */
78 if (t.type() != Token::NUMBER)
79 return false;
80
81 /* read numeric value and skip the corresponding tokens */
82 Number_of_bytes value;
83 size_t n = ascii_to(t.start(), &value);
84
85 if (n == 0)
86 return false;
87
88 t = Token(t.start() + n);
89 *out_value = value;
90
91 /* check for strange characters at the end of the number */
92 t = t.eat_whitespace();
93 if (t && (t[0] != `,`)) return false;
94
95 return true;
96 }
97
98 public:
99
100 /**
101 * Construct argument from Token(s)
102 */
103 Arg(Token t = Token()) : _key(t), _value(0)
104 {
105 for (; t && (t[0] != `,`); t = t.next().eat_whitespace())
106 if (t[0] == `=`) {
107 _value = t.next().eat_whitespace();
108 break;
109 }
110 }
111
112 inline bool valid() const { return _key; }
113
114 unsigned long ulong_value(unsigned long default_value) const
115 {
116 unsigned long value = 0;
117 int sign = 1;
118
119 bool valid = read_ulong(&value, &sign);
120 if (sign < 0)
121 return default_value;
122
123 return valid ? value : default_value;
124 }
125
126 long long_value(long default_value) const
127 {
128 unsigned long value = 0;
129 int sign = 1;
130
131 bool valid = read_ulong(&value, &sign);
132
133 /* FIXME we should check for overflows here! */
134 return valid ? sign*value : default_value;
135 }
136
137 bool bool_value(bool default_value) const
138 {
139 /* check for known idents */
140 if (_value.type() == Token::IDENT) {
141 char *p = _value.start();
142 size_t l = _value.len();
143
144 if (!strcmp(p, "yes", l)) return true;
145 if (!strcmp(p, "true", l)) return true;
146 if (!strcmp(p, "on", l)) return true;
147
148 if (!strcmp(p, "no", l)) return false;
149 if (!strcmp(p, "false", l)) return false;
150 if (!strcmp(p, "off", l)) return false;
151
152 /* saxony mode ;) */
153 if (!strcmp(p, "nu", l)) return true;
154 if (!strcmp(p, "nee", l)) return false;
155
156 return default_value;
157 }
158
159 /* read values 0 (false) / !0 (true) */
160 unsigned long value;
161 int sign;
162 bool valid = read_ulong(&value, &sign);
163
164 return valid ? value : default_value;
165 }
166
167 void key(char *dst, size_t dst_len) const
168 {
169 _key.string(dst, dst_len);
170 }
171
172 void string(char *dst, size_t dst_len, const char *default_string) const
173 {
174 /* check for one-word string w/o quotes */
175 if (_value.type() == Token::IDENT) {
176 size_t len = min(dst_len - 1, _value.len());
177 memcpy(dst, _value.start(), len);
178 dst[len] = 0;
179 return;
180 }
181
182 /* stop here if _value is not a string */
183 if (_value.type() != Token::STRING) {
184 strncpy(dst, default_string, dst_len);
185 return;
186 }
187
188 /* unpack string to dst */
189 size_t num_chars = min(dst_len - 1, _value.len());
190 unpack_string(_value.start(), dst, num_chars);
191 }
192 };
193
194
195 class Arg_string
196 {
197 typedef Arg::Token Token;
198
199 private:
200
201 static Token _next_key(Token t)
202 {
203 for (; t; t = t.next().eat_whitespace())
204
205 /* if we find a comma, return token after comma */
206 if (t[0] == `,`) return t.next().eat_whitespace();
207
208 return Token();
209 }
210
211 /**
212 * Find key token in argument string
213 */
214 static Token _find_key(const char *args, const char *key)
215 {
216 for (Token t(args); t; t = _next_key(t))
217
218 /* check if key matches */
219 if ((t.type() == Token::IDENT) && !strcmp(key, t.start(), t.len()))
220 return t;
221
222 return Token();
223 }
224
225 /**
226 * Append source string to destination string
227 *
228 * NOTE: check string length before calling this function!
229 *
230 * \return last character of result string
231 */
232 static char *_append(char *dst, const char *src)
233 {
234 unsigned src_len = strlen(src);
235 while (*dst) dst++;
236 memcpy(dst, src, src_len + 1);
237 return dst + src_len;
238 }
239
240 public:
241
242 /**
243 * Find argument by its key
244 */
245 static Arg find_arg(const char *args, const char *key) {
246 return (args && key) ? Arg(_find_key(args, key)) : Arg(); }
247
248 static Arg first_arg(const char *args) {
249 return Arg(Token(args)); }
250
251 /**
252 * Remove argument with the specified key
253 */
254 static bool remove_arg(char *args, const char *key)
255 {
256 if (!args || !key) return false;
257
258 Token beg = _find_key(args, key);
259 Token next = _next_key(beg);
260
261 /* no such key to remove - we are done */
262 if (!beg) return true;
263
264 /* if argument is the last one, null-terminate string right here */
265 if (!next) {
266
267 /* eat all pending whitespaces at the end of the string */
268 char *s = max(beg.start() - 1, args);
269 while (s > args && (*s == ` `)) s--;
270
271 /* write string-terminating zero */
272 *s = 0;
273 } else
274 memcpy(beg.start(), next.start(), strlen(next.start()) + 1);
275
276 return true;
277 }
278
279 /**
280 * Add new argument
281 */
282 static bool add_arg(char *args, unsigned args_len,
283 const char *key, const char *value)
284 {
285 if (!args || !key || !value) return false;
286
287 unsigned old_len = strlen(args);
288
289 /* check if args string has enough capacity */
290 if (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 _append(_append(_append(args, key), "="), value);
299 return true;
300 }
301
302 /**
303 * Assign new value to argument
304 */
305 static bool set_arg(char *args, unsigned args_len,
306 const char *key, const char *value)
307 {
308 return remove_arg(args, key) && add_arg(args, args_len, key, value);
309 }
310
311 /**
312 * Assign new integer argument
313 */
314 static bool set_arg(char *args, unsigned args_len,
315 const char *key, int value)
316 {
317 enum { STRING_LONG_MAX = 32 };
318 char buf[STRING_LONG_MAX];
319 snprintf(buf, sizeof(buf), "%d", value);
320 return remove_arg(args, key) && add_arg(args, args_len, key, buf);
321 }
322 };
323 }
324
325 #endif /* _INCLUDE__UTIL__ARG_STRING_H_ */