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.next();
  75           else if (t[0] == `-`) {
  76              *out_sign = -1;
  77              = 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 (== 0)
  89              return false;

  90  
  91           = Token(t.start() + n);
  92           *out_value = value;
  93  
  94           /* check for strange characters at the end of the number */
  95           = t.eat_whitespace();
  96           if (&& (t[0] != `,`)) return false;
  97  
  98           return true;

  99        }

 100  
 101     public:
 102  
 103        /**
 104         * Construct argument from Token(s)
 105         */

 106        Arg(Token = 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 *= max(beg.start() - 1, args);
 266              while (> args && (*== ` `)) s--;
 267  
 268              /* write string-terminating zero */
 269              *= 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_ */