text: /*
   * Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements.  See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership.  The ASF licenses this file
   * to you under the Apache License, Version 2.0 (the
   * "License"); you may not use this file except in compliance
   * with the License.  You may obtain a copy of the License at
   *
   *  http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing,
   * software distributed under the License is distributed on an
   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   * KIND, either express or implied.  See the License for the
   * specific language governing permissions and limitations
   * under the License.
   */
  
  #include <string.h>
  #include <stdlib.h>
  #include <limits.h>
  #include <stdarg.h>
  #include <errno.h>
  #include <assert.h>
  #include "console/console.h"
  #include "host/ble_hs.h"
  #include "host/ble_uuid.h"
  #include "host/ble_eddystone.h"
  #include "cmd.h"
  #include "btshell.h"
  
  #define CMD_MAX_ARGS        16
  
  static char *cmd_args[CMD_MAX_ARGS][2];
  static int cmd_num_args;
  
  int
  parse_arg_find_idx(const char *key)
  {
      int i;
  
      for (i = 0; i < cmd_num_args; i++) {
          if (strcmp(cmd_args[i][0], key) == 0) {
              return i;
          }
      }
  
      return -1;
  }
  
  char *
  parse_arg_peek(const char *key)
  {
      int i;
  
      for (i = 0; i < cmd_num_args; i++) {
          if (strcmp(cmd_args[i][0], key) == 0) {
              return cmd_args[i][1];
          }
      }
  
      return NULL;
  }
  
  char *
  parse_arg_extract(const char *key)
  {
      int i;
  
      for (i = 0; i < cmd_num_args; i++) {
          if (strcmp(cmd_args[i][0], key) == 0) {
              /* Erase parameter. */
              cmd_args[i][0][0] = '\0';
  
              return cmd_args[i][1];
          }
      }
  
      return NULL;
  }
  
  /**
   * Determines which number base to use when parsing the specified numeric
   * string.  This just avoids base '0' so that numbers don't get interpreted as
   * octal.
   */
  static int
  parse_arg_long_base(char *sval)
  {
      if (sval[0] == '0' && sval[1] == 'x') {
          return 0;
      } else {
          return 10;
      }
  }
  
  long
  parse_long_bounds(char *sval, long min, long max, int *out_status)
  {
      char *endptr;
      long lval;
  
      lval = strtol(sval, &endptr, parse_arg_long_base(sval));
      if (sval[0] != '\0' && *endptr == '\0' &&
          lval >= min && lval <= max) {
  
          *out_status = 0;
          return lval;
      }
  
      *out_status = EINVAL;
      return 0;
  }
  
  long
  parse_arg_long_bounds_peek(char *name, long min, long max, int *out_status)
  {
      char *sval;
  
      sval = parse_arg_peek(name);
      if (sval == NULL) {
          *out_status = ENOENT;
          return 0;
      }
      return parse_long_bounds(sval, min, max, out_status);
  }
  
  long
  parse_arg_long_bounds(char *name, long min, long max, int *out_status)
  {
      char *sval;
  
      sval = parse_arg_extract(name);
      if (sval == NULL) {
          *out_status = ENOENT;
          return 0;
      }
      return parse_long_bounds(sval, min, max, out_status);
  }
  
  long
  parse_arg_long_bounds_dflt(char *name, long min, long max,
                                long dflt, int *out_status)
  {
      long val;
      int rc;
  
      val = parse_arg_long_bounds(name, min, max, &rc);
      if (rc == ENOENT) {
          rc = 0;
          val = dflt;
      }
  
      *out_status = rc;
  
      return val;
  }
  
  uint64_t
  parse_arg_uint64_bounds(char *name, uint64_t min, uint64_t max, int *out_status)
  {
      char *endptr;
      char *sval;
      uint64_t lval;
  
      sval = parse_arg_extract(name);
      if (sval == NULL) {
          *out_status = ENOENT;
          return 0;
      }
  
      lval = strtoull(sval, &endptr, parse_arg_long_base(sval));
      if (sval[0] != '\0' && *endptr == '\0' &&
          lval >= min && lval <= max) {
  
          *out_status = 0;
          return lval;
      }
  
      *out_status = EINVAL;
      return 0;
  }
  
  long
  parse_arg_long(char *name, int *out_status)
  {
      return parse_arg_long_bounds(name, LONG_MIN, LONG_MAX, out_status);
  }
  
  uint8_t
  parse_arg_bool(char *name, int *out_status)
  {
      return parse_arg_long_bounds(name, 0, 1, out_status);
  }
  
  uint8_t
  parse_arg_bool_dflt(char *name, uint8_t dflt, int *out_status)
  {
      return parse_arg_long_bounds_dflt(name, 0, 1, dflt, out_status);
  }
  
  uint8_t
  parse_arg_uint8(char *name, int *out_status)
  {
      return parse_arg_long_bounds(name, 0, UINT8_MAX, out_status);
  }
  
  uint16_t
  parse_arg_uint16(char *name, int *out_status)
  {
      return parse_arg_long_bounds(name, 0, UINT16_MAX, out_status);
  }
  
  uint16_t
  parse_arg_uint16_peek(char *name, int *out_status)
  {
      return parse_arg_long_bounds_peek(name, 0, UINT16_MAX, out_status);
  }
  
  uint32_t
  parse_arg_uint32(char *name, int *out_status)
  {
      return parse_arg_uint64_bounds(name, 0, UINT32_MAX, out_status);
  }
  
  uint64_t
  parse_arg_uint64(char *name, int *out_status)
  {
      return parse_arg_uint64_bounds(name, 0, UINT64_MAX, out_status);
  }
  
  uint8_t
  parse_arg_uint8_dflt(char *name, uint8_t dflt, int *out_status)
  {
      uint8_t val;
      int rc;
  
      val = parse_arg_uint8(name, &rc);
      if (rc == ENOENT) {
          val = dflt;
          rc = 0;
      }
  
      *out_status = rc;
      return val;
  }
  
  uint16_t
  parse_arg_uint16_dflt(char *name, uint16_t dflt, int *out_status)
  {
      uint16_t val;
      int rc;
  
      val = parse_arg_uint16(name, &rc);
      if (rc == ENOENT) {
          val = dflt;
          rc = 0;
      }
  
      *out_status = rc;
      return val;
  }
  
  uint32_t
  parse_arg_uint32_dflt(char *name, uint32_t dflt, int *out_status)
  {
      uint32_t val;
      int rc;
  
      val = parse_arg_uint32(name, &rc);
      if (rc == ENOENT) {
          val = dflt;
          rc = 0;
      }
  
      *out_status = rc;
      return val;
  }
  
  const struct kv_pair *
  parse_kv_find(const struct kv_pair *kvs, char *name)
  {
      const struct kv_pair *kv;
      int i;
  
      for (i = 0; kvs[i].key != NULL; i++) {
          kv = kvs + i;
          if (strcmp(name, kv->key) == 0) {
              return kv;
          }
      }
  
      return NULL;
  }
  
  int
  parse_arg_kv(char *name, const struct kv_pair *kvs, int *out_status)
  {
      const struct kv_pair *kv;
      char *sval;
  
      sval = parse_arg_extract(name);
      if (sval == NULL) {
          *out_status = ENOENT;
          return -1;
      }
  
      kv = parse_kv_find(kvs, sval);
      if (kv == NULL) {
          *out_status = EINVAL;
          return -1;
      }
  
      *out_status = 0;
      return kv->val;
  }
  
  int
  parse_arg_kv_dflt(char *name, const struct kv_pair *kvs, int def_val,
                       int *out_status)
  {
      int val;
      int rc;
  
      val = parse_arg_kv(name, kvs, &rc);
      if (rc == ENOENT) {
          rc = 0;
          val = def_val;
      }
  
      *out_status = rc;
  
      return val;
  }
  
  
  static int
  parse_arg_byte_stream_delim(char *sval, char *delims, int max_len,
                              uint8_t *dst, int *out_len)
  {
      unsigned long ul;
      char *endptr;
      char *token;
      int i;
  
      i = 0;
      for (token = strtok(sval, delims);
           token != NULL;
           token = strtok(NULL, delims)) {
  
          if (i >= max_len) {
              return EINVAL;
          }
  
          ul = strtoul(token, &endptr, 16);
          if (sval[0] == '\0' || *endptr != '\0' || ul > UINT8_MAX) {
              return -1;
          }
  
          dst[i] = ul;
          i++;
      }
  
      *out_len = i;
  
      return 0;
  }
  
  int
  parse_arg_byte_stream(char *name, int max_len, uint8_t *dst, int *out_len)
  {
      char *sval;
  
      sval = parse_arg_extract(name);
      if (sval == NULL) {
          return ENOENT;
      }
  
      return parse_arg_byte_stream_delim(sval, ":-", max_len, dst, out_len);
  }
  
  int
  parse_arg_byte_stream_exact_length(char *name, uint8_t *dst, int len)
  {
      int actual_len;
      int rc;
  
      rc = parse_arg_byte_stream(name, len, dst, &actual_len);
      if (rc != 0) {
          return rc;
      }
  
      if (actual_len != len) {
          return EINVAL;
      }
  
      return 0;
  }
  
  static void
  parse_reverse_bytes(uint8_t *bytes, int len)
  {
      uint8_t tmp;
      int i;
  
      for (i = 0; i < len / 2; i++) {
          tmp = bytes[i];
          bytes[i] = bytes[len - i - 1];
          bytes[len - i - 1] = tmp;
      }
  }
  
  int
  parse_arg_mac(char *name, uint8_t *dst)
  {
      int rc;
  
      rc = parse_arg_byte_stream_exact_length(name, dst, 6);
      if (rc != 0) {
          return rc;
      }
  
      parse_reverse_bytes(dst, 6);
  
      return 0;
  }
  
  int
  parse_arg_uuid(char *str, ble_uuid_any_t *uuid)
  {
      uint16_t uuid16;
      uint8_t val[16];
      int len;
      int rc;
  
      uuid16 = parse_arg_uint16_peek(str, &rc);
      switch (rc) {
      case ENOENT:
          parse_arg_extract(str);
          return ENOENT;
  
      case 0:
          len = 2;
          val[0] = uuid16;
          val[1] = uuid16 >> 8;
          parse_arg_extract(str);
          break;
  
      default:
          len = 16;
          rc = parse_arg_byte_stream_exact_length(str, val, 16);
          if (rc != 0) {
              return EINVAL;
          }
          parse_reverse_bytes(val, 16);
          break;
      }
  
      rc = ble_uuid_init_from_buf(uuid, val, len);
      if (rc != 0) {
          return EINVAL;
      } else {
          return 0;
      }
  }
  
  int
  parse_arg_all(int argc, char **argv)
  {
      char *key;
      char *val;
      int i;
  
      cmd_num_args = 0;
  
      for (i = 0; i < argc; i++) {
          key = strtok(argv[i], "=");
          val = strtok(NULL, "=");
  
          if (key != NULL && val != NULL) {
              if (strlen(key) == 0) {
                  console_printf("Error: invalid argument: %s\n", argv[i]);
                  return -1;
              }
  
              if (cmd_num_args >= CMD_MAX_ARGS) {
                  console_printf("Error: too many arguments");
                  return -1;
              }
  
              cmd_args[cmd_num_args][0] = key;
              cmd_args[cmd_num_args][1] = val;
              cmd_num_args++;
          }
      }
  
      return 0;
  }
  
  int
  parse_eddystone_url(char *full_url, uint8_t *out_scheme, char *out_body,
                      uint8_t *out_body_len, uint8_t *out_suffix)
  {
      static const struct {
          char *s;
          uint8_t scheme;
      } schemes[] = {
          { "http://www.", BLE_EDDYSTONE_URL_SCHEME_HTTP_WWW },
          { "https://www.", BLE_EDDYSTONE_URL_SCHEME_HTTPS_WWW },
          { "http://", BLE_EDDYSTONE_URL_SCHEME_HTTP },
          { "https://", BLE_EDDYSTONE_URL_SCHEME_HTTPS },
      };
  
      static const struct {
          char *s;
          uint8_t code;
      } suffixes[] = {
          { ".com/", BLE_EDDYSTONE_URL_SUFFIX_COM_SLASH },
          { ".org/", BLE_EDDYSTONE_URL_SUFFIX_ORG_SLASH },
          { ".edu/", BLE_EDDYSTONE_URL_SUFFIX_EDU_SLASH },
          { ".net/", BLE_EDDYSTONE_URL_SUFFIX_NET_SLASH },
          { ".info/", BLE_EDDYSTONE_URL_SUFFIX_INFO_SLASH },
          { ".biz/", BLE_EDDYSTONE_URL_SUFFIX_BIZ_SLASH },
          { ".gov/", BLE_EDDYSTONE_URL_SUFFIX_GOV_SLASH },
          { ".com", BLE_EDDYSTONE_URL_SUFFIX_COM },
          { ".org", BLE_EDDYSTONE_URL_SUFFIX_ORG },
          { ".edu", BLE_EDDYSTONE_URL_SUFFIX_EDU },
          { ".net", BLE_EDDYSTONE_URL_SUFFIX_NET },
          { ".info", BLE_EDDYSTONE_URL_SUFFIX_INFO },
          { ".biz", BLE_EDDYSTONE_URL_SUFFIX_BIZ },
          { ".gov", BLE_EDDYSTONE_URL_SUFFIX_GOV },
      };
  
      char *prefix;
      char *suffix;
      int full_url_len;
      int prefix_len;
      int suffix_len;
      int suffix_idx;
      int rc;
      int i;
  
      full_url_len = strlen(full_url);
  
      rc = BLE_HS_EINVAL;
      for (i = 0; i < sizeof schemes / sizeof schemes[0]; i++) {
          prefix = schemes[i].s;
          prefix_len = strlen(schemes[i].s);
  
          if (full_url_len >= prefix_len &&
              memcmp(full_url, prefix, prefix_len) == 0) {
  
              *out_scheme = i;
              rc = 0;
              break;
          }
      }
      if (rc != 0) {
          return rc;
      }
  
      rc = BLE_HS_EINVAL;
      for (i = 0; i < sizeof suffixes / sizeof suffixes[0]; i++) {
          suffix = suffixes[i].s;
          suffix_len = strlen(suffixes[i].s);
  
          suffix_idx = full_url_len - suffix_len;
          if (suffix_idx >= prefix_len &&
              memcmp(full_url + suffix_idx, suffix, suffix_len) == 0) {
  
              *out_suffix = i;
              rc = 0;
              break;
          }
      }
      if (rc != 0) {
          *out_suffix = BLE_EDDYSTONE_URL_SUFFIX_NONE;
          *out_body_len = full_url_len - prefix_len;
      } else {
          *out_body_len = full_url_len - prefix_len - suffix_len;
      }
  
      memcpy(out_body, full_url + prefix_len, *out_body_len);
  
      return 0;
  }
  