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.next();
  72              else if (t[0] == `-`) {
  73                 *out_sign = -1;
  74                 = 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 (== 0)
  86                 return false;

  87  
  88              = Token(t.start() + n);
  89              *out_value = value;
  90  
  91              /* check for strange characters at the end of the number */
  92              = t.eat_whitespace();
  93              if (&& (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   *= _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 *= max(beg.start() - 1, args);
 269                 while (> args && (*== ` `)) s--;
 270  
 271                 /* write string-terminating zero */
 272                 *= 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_ */