/* * \brief Argument list string handling * \author Norman Feske * \date 2006-05-22 * * Each argument has the form: * * ! = * * Key is an identifier that begins with a letter or underline * and may also contain digits. * * A list of arguments is specified by using comma as separator, * whereas the first argument is considered as the weakest. If * we replace an argument value of an existing argument, the * existing argument is removed and we append a new argument at * the end of the string. */ /* * Copyright (C) 2006-2017 Genode Labs GmbH * * This file is part of the Genode OS framework, which is distributed * under the terms of the GNU Affero General Public License version 3. */ #ifndef _INCLUDE__UTIL__ARG_STRING_H_ #define _INCLUDE__UTIL__ARG_STRING_H_ #include #include #include namespace Genode { class Arg_string; class Arg; } class Genode::Arg { /** * Define tokenizer used for argument-string parsing * * Argument-string tokens accept C-style identifiers. */ typedef ::Genode::Token Token; friend class Arg_string; private: Token _key; Token _value; /** * Return long value of argument * * \param out_value argument converted to unsigned long value * \param out_sign 1 if positive; -1 if negative * \return true if no syntactic anomaly occured * * This method handles the numberic modifiers G (2^30), * M (2^20), and K (2^10). */ bool read_ulong(unsigned long *out_value, int *out_sign) const { Token t = _value; /* check for sign; default is positive */ *out_sign = 1; if (t[0] == '+') t = t.next(); else if (t[0] == '-') { *out_sign = -1; t = t.next(); } /* stop if token after sign is no number */ if (t.type() != Token::NUMBER) return false; /* read numeric value and skip the corresponding tokens */ Number_of_bytes value; size_t n = ascii_to(t.start(), value); if (n == 0) return false; t = Token(t.start() + n); *out_value = value; /* check for strange characters at the end of the number */ t = t.eat_whitespace(); if (t && (t[0] != ',')) return false; return true; } public: /** * Construct argument from Token(s) */ Arg(Token t = Token()) : _key(t), _value(0) { for (; t && (t[0] != ','); t = t.next().eat_whitespace()) if (t[0] == '=') { _value = t.next().eat_whitespace(); break; } } inline bool valid() const { return _key; } unsigned long ulong_value(unsigned long default_value) const { unsigned long value = 0; int sign = 1; bool valid = read_ulong(&value, &sign); if (sign < 0) return default_value; return valid ? value : default_value; } long long_value(long default_value) const { unsigned long value = 0; int sign = 1; bool valid = read_ulong(&value, &sign); /* FIXME we should check for overflows here! */ return valid ? sign*value : default_value; } bool bool_value(bool default_value) const { bool result = default_value; switch (_value.type()) { /* result is passed to 'ascii_to' by reference */ case Token::IDENT:; if (ascii_to(_value.start(), result) == _value.len()) return result; return default_value; case Token::STRING: if (ascii_to(_value.start()+1, result) == _value.len()-2) return result; return default_value; default: /* read values 0 (false) / !0 (true) */ unsigned long value; int sign; bool valid = read_ulong(&value, &sign); return valid ? value : default_value; } } void key(char *dst, size_t dst_len) const { _key.string(dst, dst_len); } void string(char *dst, size_t dst_len, const char *default_string) const { /* check for one-word string w/o quotes */ if (_value.type() == Token::IDENT) { size_t len = min(dst_len - 1, _value.len()); memcpy(dst, _value.start(), len); dst[len] = 0; return; } /* stop here if _value is not a string */ if (_value.type() != Token::STRING) { copy_cstring(dst, default_string, dst_len); return; } /* unpack string to dst */ size_t num_chars = min(dst_len - 1, _value.len()); if (unpack_string(_value.start(), dst, num_chars) == 0UL) copy_cstring(dst, default_string, dst_len); } /** * Retrieve a dataspace (page) aligned size argument */ size_t aligned_size() const { unsigned long value = ulong_value(0); return align_addr(value, 12); } }; class Genode::Arg_string { typedef Arg::Token Token; private: static Token _next_key(Token t) { for (; t; t = t.next().eat_whitespace()) /* if we find a comma, return token after comma */ if (t[0] == ',') return t.next().eat_whitespace(); return Token(); } /** * Find key token in argument string */ static Token _find_key(const char *args, const char *key) { for (Token t(args); t; t = _next_key(t)) /* check if key matches */ if ((t.type() == Token::IDENT) && !strcmp(key, t.start(), t.len())) return t; return Token(); } /** * Append source string to destination string * * NOTE: check string length before calling this method! * * \return last character of result string */ static char *_append(char *dst, const char *src) { size_t const src_len = strlen(src); while (*dst) dst++; memcpy(dst, src, src_len + 1); return dst + src_len; } public: /** * Find argument by its key */ static Arg find_arg(const char *args, const char *key) { return (args && key) ? Arg(_find_key(args, key)) : Arg(); } static Arg first_arg(const char *args) { return Arg(Token(args)); } /** * Remove argument with the specified key */ static bool remove_arg(char *args, const char *key) { if (!args || !key) return false; Token beg = _find_key(args, key); Token next = _next_key(beg); /* no such key to remove - we are done */ if (!beg) return true; /* if argument is the last one, null-terminate string right here */ if (!next) { /* eat all pending whitespaces at the end of the string */ char *s = max(beg.start() - 1, args); while (s > args && (*s == ' ')) s--; /* write string-terminating zero */ *s = 0; } else memcpy(beg.start(), next.start(), strlen(next.start()) + 1); return true; } /** * Add new argument */ static bool add_arg(char *args, size_t const args_len, const char * const key, const char * const value, Token::Type const type = Token::Type::IDENT) { if (!args || !key || !value) return false; size_t const old_len = strlen(args); /* check if args string has enough capacity */ if ((type == Token::Type::STRING && old_len + strlen(key) + strlen(value) + 4 > args_len) || (old_len + strlen(key) + strlen(value) + 2 > args_len)) return false; args += old_len; if (old_len) args = _append(args, ", "); if (type == Token::Type::STRING) _append(_append(_append(_append(args, key), "=\""), value), "\""); else _append(_append(_append(args, key), "="), value); return true; } /** * Assign new value to argument */ static bool set_arg(char *args, size_t args_len, const char *key, const char *value) { return remove_arg(args, key) && add_arg(args, args_len, key, value); } /** * Assign new integer argument */ static bool set_arg(char *args, size_t args_len, const char *key, int value) { enum { STRING_LONG_MAX = 32 }; char buf[STRING_LONG_MAX]; snprintf(buf, sizeof(buf), "%d", value); return remove_arg(args, key) && add_arg(args, args_len, key, buf); } /** * Assign new string argument */ static bool set_arg_string(char *args, size_t args_len, const char *key, const char *value) { return remove_arg(args, key) && add_arg(args, args_len, key, value, Token::Type::STRING); } }; #endif /* _INCLUDE__UTIL__ARG_STRING_H_ */