text: /*
   *  Copyright (c) 2016, Nest Labs, Inc.
   *  All rights reserved.
   *
   *  Redistribution and use in source and binary forms, with or without
   *  modification, are permitted provided that the following conditions are met:
   *  1. Redistributions of source code must retain the above copyright
   *     notice, this list of conditions and the following disclaimer.
   *  2. Redistributions in binary form must reproduce the above copyright
   *     notice, this list of conditions and the following disclaimer in the
   *     documentation and/or other materials provided with the distribution.
   *  3. Neither the name of the copyright holder nor the
   *     names of its contributors may be used to endorse or promote products
   *     derived from this software without specific prior written permission.
   *
   *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
   *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
   *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   *  POSSIBILITY OF SUCH DAMAGE.
   */
  
  /**
   * @file
   *   This file implements the CLI interpreter.
   */
  
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  
  #include <openthread.h>
  #include <openthread-diag.h>
  
  #include "cli.hpp"
  #include "cli_dataset.hpp"
  #include <common/encoding.hpp>
  #include <common/new.hpp>
  #include <platform/random.h>
  #include <platform/uart.h>
  
  using Thread::Encoding::BigEndian::HostSwap16;
  using Thread::Encoding::BigEndian::HostSwap32;
  
  namespace Thread {
  namespace Cli {
  
  const struct Command Interpreter::sCommands[] =
  {
      { "help", &ProcessHelp },
      { "blacklist", &ProcessBlacklist },
      { "channel", &ProcessChannel },
      { "child", &ProcessChild },
      { "childmax", &ProcessChildMax },
      { "childtimeout", &ProcessChildTimeout },
      { "contextreusedelay", &ProcessContextIdReuseDelay },
      { "counter", &ProcessCounters },
      { "dataset", &ProcessDataset },
      { "discover", &ProcessDiscover },
      { "eidcache", &ProcessEidCache },
      { "extaddr", &ProcessExtAddress },
      { "extpanid", &ProcessExtPanId },
      { "ifconfig", &ProcessIfconfig },
      { "ipaddr", &ProcessIpAddr },
      { "keysequence", &ProcessKeySequence },
      { "leaderdata", &ProcessLeaderData },
      { "leaderpartitionid", &ProcessLeaderPartitionId },
      { "leaderweight", &ProcessLeaderWeight },
      { "linkquality", &ProcessLinkQuality },
      { "masterkey", &ProcessMasterKey },
      { "mode", &ProcessMode },
      { "netdataregister", &ProcessNetworkDataRegister },
      { "networkidtimeout", &ProcessNetworkIdTimeout },
      { "networkname", &ProcessNetworkName },
      { "panid", &ProcessPanId },
      { "parent", &ProcessParent },
      { "ping", &ProcessPing },
      { "pollperiod", &ProcessPollPeriod },
      { "promiscuous", &ProcessPromiscuous },
      { "prefix", &ProcessPrefix },
      { "releaserouterid", &ProcessReleaseRouterId },
      { "reset", &ProcessReset },
      { "rloc16", &ProcessRloc16 },
      { "route", &ProcessRoute },
      { "router", &ProcessRouter },
      { "routerrole", &ProcessRouterRole },
      { "routerupgradethreshold", &ProcessRouterUpgradeThreshold },
      { "scan", &ProcessScan },
      { "singleton", &ProcessSingleton },
      { "state", &ProcessState },
      { "thread", &ProcessThread },
      { "version", &ProcessVersion },
      { "whitelist", &ProcessWhitelist },
  #if OPENTHREAD_ENABLE_DIAG
      { "diag", &ProcessDiag },
  #endif
  };
  
  static otDEFINE_ALIGNED_VAR(sPingTimerBuf, sizeof(Timer), uint64_t);
  Timer *Interpreter::sPingTimer;
  
  Ip6::MessageInfo Interpreter::sMessageInfo;
  Server *Interpreter::sServer;
  uint8_t Interpreter::sEchoRequest[1500];
  uint16_t Interpreter::sLength;
  uint16_t Interpreter::sCount;
  uint32_t Interpreter::sInterval;
  
  static otNetifAddress sAutoAddresses[8];
  
  void Interpreter::Init(void)
  {
      Ip6::Icmp::SetEchoReplyHandler(&HandleEchoResponse, NULL);
      sPingTimer = new(&sPingTimerBuf) Timer(&HandlePingTimer, NULL);
      sLength = 8;
      sCount = 1;
      sInterval = 1000;
      otSetStateChangedCallback(&HandleNetifStateChanged, NULL);
  }
  
  int Interpreter::Hex2Bin(const char *aHex, uint8_t *aBin, uint16_t aBinLength)
  {
      size_t hexLength = strlen(aHex);
      const char *hexEnd = aHex + hexLength;
      uint8_t *cur = aBin;
      uint8_t numChars = hexLength & 1;
      uint8_t byte = 0;
  
      if ((hexLength + 1) / 2 > aBinLength)
      {
          return -1;
      }
  
      while (aHex < hexEnd)
      {
          if ('A' <= *aHex && *aHex <= 'F')
          {
              byte |= 10 + (*aHex - 'A');
          }
          else if ('a' <= *aHex && *aHex <= 'f')
          {
              byte |= 10 + (*aHex - 'a');
          }
          else if ('0' <= *aHex && *aHex <= '9')
          {
              byte |= *aHex - '0';
          }
          else
          {
              return -1;
          }
  
          aHex++;
          numChars++;
  
          if (numChars >= 2)
          {
              numChars = 0;
              *cur++ = byte;
              byte = 0;
          }
          else
          {
              byte <<= 4;
          }
      }
  
      return static_cast<int>(cur - aBin);
  }
  
  void Interpreter::AppendResult(ThreadError error)
  {
      if (error == kThreadError_None)
      {
          sServer->OutputFormat("Done\r\n");
      }
      else
      {
          sServer->OutputFormat("Error %d\r\n", error);
      }
  }
  
  void Interpreter::OutputBytes(const uint8_t *aBytes, uint8_t aLength)
  {
      for (int i = 0; i < aLength; i++)
      {
          sServer->OutputFormat("%02x", aBytes[i]);
      }
  }
  
  ThreadError Interpreter::ParseLong(char *argv, long &value)
  {
      char *endptr;
      value = strtol(argv, &endptr, 0);
      return (*endptr == '\0') ? kThreadError_None : kThreadError_Parse;
  }
  
  ThreadError Interpreter::ParseUnsignedLong(char *argv, unsigned long &value)
  {
      char *endptr;
      value = strtoul(argv, &endptr, 0);
      return (*endptr == '\0') ? kThreadError_None : kThreadError_Parse;
  }
  
  void Interpreter::ProcessHelp(int argc, char *argv[])
  {
      for (unsigned int i = 0; i < sizeof(sCommands) / sizeof(sCommands[0]); i++)
      {
          sServer->OutputFormat("%s\r\n", sCommands[i].mName);
      }
  
      (void)argc;
      (void)argv;
  }
  
  void Interpreter::ProcessBlacklist(int argc, char *argv[])
  {
      ThreadError error = kThreadError_None;
      otMacBlacklistEntry entry;
      int argcur = 0;
      uint8_t extAddr[8];
  
      if (argcur >= argc)
      {
          if (otIsMacBlacklistEnabled())
          {
              sServer->OutputFormat("Enabled\r\n");
          }
          else
          {
              sServer->OutputFormat("Disabled\r\n");
          }
  
          for (uint8_t i = 0; ; i++)
          {
              if (otGetMacBlacklistEntry(i, &entry) != kThreadError_None)
              {
                  break;
              }
  
              if (entry.mValid == false)
              {
                  continue;
              }
  
              OutputBytes(entry.mExtAddress.m8, OT_EXT_ADDRESS_SIZE);
  
              sServer->OutputFormat("\r\n");
          }
      }
      else if (strcmp(argv[argcur], "add") == 0)
      {
          VerifyOrExit(++argcur < argc, error = kThreadError_Parse);
          VerifyOrExit(Hex2Bin(argv[argcur], extAddr, sizeof(extAddr)) == sizeof(extAddr), error = kThreadError_Parse);
  
          otAddMacBlacklist(extAddr);
          VerifyOrExit(otAddMacBlacklist(extAddr) == kThreadError_None, error = kThreadError_Parse);
      }
      else if (strcmp(argv[argcur], "clear") == 0)
      {
          otClearMacBlacklist();
      }
      else if (strcmp(argv[argcur], "disable") == 0)
      {
          otDisableMacBlacklist();
      }
      else if (strcmp(argv[argcur], "enable") == 0)
      {
          otEnableMacBlacklist();
      }
      else if (strcmp(argv[argcur], "remove") == 0)
      {
          VerifyOrExit(++argcur < argc, error = kThreadError_Parse);
          VerifyOrExit(Hex2Bin(argv[argcur], extAddr, sizeof(extAddr)) == sizeof(extAddr), error = kThreadError_Parse);
          otRemoveMacBlacklist(extAddr);
      }
  
  exit:
      AppendResult(error);
  }
  
  void Interpreter::ProcessChannel(int argc, char *argv[])
  {
      ThreadError error = kThreadError_None;
      long value;
  
      if (argc == 0)
      {
          sServer->OutputFormat("%d\r\n", otGetChannel());
      }
      else
      {
          SuccessOrExit(error = ParseLong(argv[0], value));
          otSetChannel(static_cast<uint8_t>(value));
      }
  
  exit:
      AppendResult(error);
  }
  
  void Interpreter::ProcessChild(int argc, char *argv[])
  {
      ThreadError error = kThreadError_None;
      otChildInfo childInfo;
      long value;
  
      VerifyOrExit(argc > 0, error = kThreadError_Parse);
  
      if (strcmp(argv[0], "list") == 0)
      {
          for (uint8_t i = 0; ; i++)
          {
              if (otGetChildInfoByIndex(i, &childInfo) != kThreadError_None)
              {
                  sServer->OutputFormat("\r\n");
                  ExitNow();
              }
  
              if (childInfo.mTimeout > 0)
              {
                  sServer->OutputFormat("%d ", childInfo.mChildId);
              }
          }
      }
  
      SuccessOrExit(error = ParseLong(argv[0], value));
      SuccessOrExit(error = otGetChildInfoById(static_cast<uint16_t>(value), &childInfo));
  
      sServer->OutputFormat("Child ID: %d\r\n", childInfo.mChildId);
      sServer->OutputFormat("Rloc: %04x\r\n", childInfo.mRloc16);
      sServer->OutputFormat("Ext Addr: ");
  
      for (size_t j = 0; j < sizeof(childInfo.mExtAddress); j++)
      {
          sServer->OutputFormat("%02x", childInfo.mExtAddress.m8[j]);
      }
  
      sServer->OutputFormat("\r\n");
      sServer->OutputFormat("Mode: ");
  
      if (childInfo.mRxOnWhenIdle)
      {
          sServer->OutputFormat("r");
      }
  
      if (childInfo.mSecureDataRequest)
      {
          sServer->OutputFormat("s");
      }
  
      if (childInfo.mFullFunction)
      {
          sServer->OutputFormat("d");
      }
  
      if (childInfo.mFullNetworkData)
      {
          sServer->OutputFormat("n");
      }
  
      sServer->OutputFormat("\r\n");
  
      sServer->OutputFormat("Net Data: %d\r\n", childInfo.mNetworkDataVersion);
      sServer->OutputFormat("Timeout: %d\r\n", childInfo.mTimeout);
      sServer->OutputFormat("Age: %d\r\n", childInfo.mAge);
      sServer->OutputFormat("LQI: %d\r\n", childInfo.mLinkQualityIn);
      sServer->OutputFormat("RSSI: %d\r\n", childInfo.mAverageRssi);
  
  exit:
      AppendResult(error);
  }
  
  void Interpreter::ProcessChildMax(int argc, char *argv[])
  {
      ThreadError error = kThreadError_None;
      long value;
  
      if (argc == 0)
      {
          sServer->OutputFormat("%d\r\n", otGetMaxAllowedChildren());
      }
      else
      {
          SuccessOrExit(error = ParseLong(argv[0], value));
          SuccessOrExit(error = otSetMaxAllowedChildren(static_cast<uint8_t>(value)));
      }
  
  exit:
      AppendResult(error);
  }
  
  void Interpreter::ProcessChildTimeout(int argc, char *argv[])
  {
      ThreadError error = kThreadError_None;
      long value;
  
      if (argc == 0)
      {
          sServer->OutputFormat("%d\r\n", otGetChildTimeout());
      }
      else
      {
          SuccessOrExit(error = ParseLong(argv[0], value));
          otSetChildTimeout(static_cast<uint32_t>(value));
      }
  
  exit:
      AppendResult(error);
  }
  
  void Interpreter::ProcessContextIdReuseDelay(int argc, char *argv[])
  {
      ThreadError error = kThreadError_None;
      long value;
  
      if (argc == 0)
      {
          sServer->OutputFormat("%d\r\n", otGetContextIdReuseDelay());
      }
      else
      {
          SuccessOrExit(ParseLong(argv[0], value));
          otSetContextIdReuseDelay(static_cast<uint32_t>(value));
      }
  
  exit:
      AppendResult(error);
  }
  
  void Interpreter::ProcessCounters(int argc, char *argv[])
  {
      if (argc == 0)
      {
          sServer->OutputFormat("mac\r\n");
          sServer->OutputFormat("Done\r\n");
      }
      else
      {
          if (strcmp(argv[0], "mac") == 0)
          {
              const otMacCounters *counters = otGetMacCounters();
              sServer->OutputFormat("TxTotal: %d\r\n", counters->mTxTotal);
              sServer->OutputFormat("    TxAckRequested: %d\r\n", counters->mTxAckRequested);
              sServer->OutputFormat("    TxAcked: %d\r\n", counters->mTxAcked);
              sServer->OutputFormat("    TxNoAckRequested: %d\r\n", counters->mTxNoAckRequested);
              sServer->OutputFormat("    TxData: %d\r\n", counters->mTxData);
              sServer->OutputFormat("    TxDataPoll: %d\r\n", counters->mTxDataPoll);
              sServer->OutputFormat("    TxBeacon: %d\r\n", counters->mTxBeacon);
              sServer->OutputFormat("    TxBeaconRequest: %d\r\n", counters->mTxBeaconRequest);
              sServer->OutputFormat("    TxOther: %d\r\n", counters->mTxOther);
              sServer->OutputFormat("    TxRetry: %d\r\n", counters->mTxRetry);
              sServer->OutputFormat("    TxErrCca: %d\r\n", counters->mTxErrCca);
              sServer->OutputFormat("RxTotal: %d\r\n", counters->mRxTotal);
              sServer->OutputFormat("    RxData: %d\r\n", counters->mRxData);
              sServer->OutputFormat("    RxDataPoll: %d\r\n", counters->mRxDataPoll);
              sServer->OutputFormat("    RxBeacon: %d\r\n", counters->mRxBeacon);
              sServer->OutputFormat("    RxBeaconRequest: %d\r\n", counters->mRxBeaconRequest);
              sServer->OutputFormat("    RxOther: %d\r\n", counters->mRxOther);
              sServer->OutputFormat("    RxWhitelistFiltered: %d\r\n", counters->mRxWhitelistFiltered);
              sServer->OutputFormat("    RxDestAddrFiltered: %d\r\n", counters->mRxDestAddrFiltered);
              sServer->OutputFormat("    RxErrNoFrame: %d\r\n", counters->mRxErrNoFrame);
              sServer->OutputFormat("    RxErrNoUnknownNeighbor: %d\r\n", counters->mRxErrUnknownNeighbor);
              sServer->OutputFormat("    RxErrInvalidSrcAddr: %d\r\n", counters->mRxErrInvalidSrcAddr);
              sServer->OutputFormat("    RxErrSec: %d\r\n", counters->mRxErrSec);
              sServer->OutputFormat("    RxErrFcs: %d\r\n", counters->mRxErrFcs);
              sServer->OutputFormat("    RxErrOther: %d\r\n", counters->mRxErrOther);
          }
      }
  }
  
  void Interpreter::ProcessDataset(int argc, char *argv[])
  {
      ThreadError error;
      error = Dataset::Process(argc, argv, *sServer);
      AppendResult(error);
  }
  
  void Interpreter::ProcessDiscover(int argc, char *argv[])
  {
      ThreadError error = kThreadError_None;
      uint32_t scanChannels = 0;
      long value;
  
      if (argc > 0)
      {
          SuccessOrExit(error = ParseLong(argv[0], value));
          scanChannels = 1 << value;
      }
  
      SuccessOrExit(error = otDiscover(scanChannels, 0, OT_PANID_BROADCAST, &HandleActiveScanResult, NULL));
      sServer->OutputFormat("| J | Network Name     | Extended PAN     | PAN  | MAC Address      | Ch | dBm | LQI |\r\n");
      sServer->OutputFormat("+---+------------------+------------------+------+------------------+----+-----+-----+\r\n");
  
      return;
  
  exit:
      AppendResult(error);
  }
  
  void Interpreter::ProcessEidCache(int argc, char *argv[])
  {
      otEidCacheEntry entry;
  
      for (uint8_t i = 0; ; i++)
      {
          SuccessOrExit(otGetEidCacheEntry(i, &entry));
  
          if (entry.mValid == false)
          {
              continue;
          }
  
          sServer->OutputFormat("%x:%x:%x:%x:%x:%x:%x:%x %04x\r\n",
                                HostSwap16(entry.mTarget.mFields.m16[0]),
                                HostSwap16(entry.mTarget.mFields.m16[1]),
                                HostSwap16(entry.mTarget.mFields.m16[2]),
                                HostSwap16(entry.mTarget.mFields.m16[3]),
                                HostSwap16(entry.mTarget.mFields.m16[4]),
                                HostSwap16(entry.mTarget.mFields.m16[5]),
                                HostSwap16(entry.mTarget.mFields.m16[6]),
                                HostSwap16(entry.mTarget.mFields.m16[7]),
                                entry.mRloc16);
      }
  
  exit:
      (void)argc;
      (void)argv;
      AppendResult(kThreadError_None);
  }
  
  void Interpreter::ProcessExtAddress(int argc, char *argv[])
  {
      ThreadError error = kThreadError_None;
  
      if (argc == 0)
      {
          OutputBytes(otGetExtendedAddress(), OT_EXT_ADDRESS_SIZE);
          sServer->OutputFormat("\r\n");
      }
      else
      {
          otExtAddress extAddress;
  
          VerifyOrExit(Hex2Bin(argv[0], extAddress.m8, sizeof(otExtAddress)) >= 0, error = kThreadError_Parse);
  
          otSetExtendedAddress(&extAddress);
      }
  
  exit:
      AppendResult(error);
  }
  
  void Interpreter::ProcessExtPanId(int argc, char *argv[])
  {
      ThreadError error = kThreadError_None;
  
      if (argc == 0)
      {
          OutputBytes(otGetExtendedPanId(), OT_EXT_PAN_ID_SIZE);
          sServer->OutputFormat("\r\n");
      }
      else
      {
          uint8_t extPanId[8];
  
          VerifyOrExit(Hex2Bin(argv[0], extPanId, sizeof(extPanId)) >= 0, error = kThreadError_Parse);
  
          otSetExtendedPanId(extPanId);
      }
  
  exit:
      AppendResult(error);
  }
  
  void Interpreter::ProcessIfconfig(int argc, char *argv[])
  {
      ThreadError error = kThreadError_None;
  
      if (argc == 0)
      {
          if (otIsInterfaceUp())
          {
              sServer->OutputFormat("up\r\n");
          }
          else
          {
              sServer->OutputFormat("down\r\n");
          }
      }
      else if (strcmp(argv[0], "up") == 0)
      {
          SuccessOrExit(error = otInterfaceUp());
      }
      else if (strcmp(argv[0], "down") == 0)
      {
          SuccessOrExit(error = otInterfaceDown());
      }
  
  exit:
      AppendResult(error);
  }
  
  ThreadError Interpreter::ProcessIpAddrAdd(int argc, char *argv[])
  {
      ThreadError error;
      otNetifAddress aAddress;
  
      VerifyOrExit(argc > 0, error = kThreadError_Parse);
  
      SuccessOrExit(error = otIp6AddressFromString(argv[0], &aAddress.mAddress));
      aAddress.mPrefixLength = 64;
      aAddress.mPreferredLifetime = 0xffffffff;
      aAddress.mValidLifetime = 0xffffffff;
      error = otAddUnicastAddress(&aAddress);
  
  exit:
      return error;
  }
  
  ThreadError Interpreter::ProcessIpAddrDel(int argc, char *argv[])
  {
      ThreadError error;
      struct otIp6Address address;
  
      VerifyOrExit(argc > 0, error = kThreadError_Parse);
  
      SuccessOrExit(error = otIp6AddressFromString(argv[0], &address));
      error = otRemoveUnicastAddress(&address);
  
  exit:
      return error;
  }
  
  void Interpreter::ProcessIpAddr(int argc, char *argv[])
  {
      ThreadError error = kThreadError_None;
  
      if (argc == 0)
      {
          for (const otNetifAddress *addr = otGetUnicastAddresses(); addr; addr = addr->mNext)
          {
              sServer->OutputFormat("%x:%x:%x:%x:%x:%x:%x:%x\r\n",
                                    HostSwap16(addr->mAddress.mFields.m16[0]),
                                    HostSwap16(addr->mAddress.mFields.m16[1]),
                                    HostSwap16(addr->mAddress.mFields.m16[2]),
                                    HostSwap16(addr->mAddress.mFields.m16[3]),
                                    HostSwap16(addr->mAddress.mFields.m16[4]),
                                    HostSwap16(addr->mAddress.mFields.m16[5]),
                                    HostSwap16(addr->mAddress.mFields.m16[6]),
                                    HostSwap16(addr->mAddress.mFields.m16[7]));
          }
      }
      else
      {
          if (strcmp(argv[0], "add") == 0)
          {
              SuccessOrExit(error = ProcessIpAddrAdd(argc - 1, argv + 1));
          }
          else if (strcmp(argv[0], "del") == 0)
          {
              SuccessOrExit(error = ProcessIpAddrDel(argc - 1, argv + 1));
          }
      }
  
  exit:
      AppendResult(error);
  }
  
  void Interpreter::ProcessKeySequence(int argc, char *argv[])
  {
      ThreadError error = kThreadError_None;
      long value;
  
      if (argc == 0)
      {
          sServer->OutputFormat("%d\r\n", otGetKeySequenceCounter());
      }
      else
      {
          SuccessOrExit(error = ParseLong(argv[0], value));
          otSetKeySequenceCounter(static_cast<uint32_t>(value));
      }
  
  exit:
      AppendResult(error);
  }
  
  void Interpreter::ProcessLeaderData(int argc, char *argv[])
  {
      ThreadError error;
      otLeaderData leaderData;
  
      SuccessOrExit(error = otGetLeaderData(&leaderData));
  
      sServer->OutputFormat("Partition ID: %u\r\n", leaderData.mPartitionId);
      sServer->OutputFormat("Weighting: %d\r\n", leaderData.mWeighting);
      sServer->OutputFormat("Data Version: %d\r\n", leaderData.mDataVersion);
      sServer->OutputFormat("Stable Data Version: %d\r\n", leaderData.mStableDataVersion);
      sServer->OutputFormat("Leader Router ID: %d\r\n", leaderData.mLeaderRouterId);
  
  exit:
      (void)argc;
      (void)argv;
      AppendResult(error);
  }
  
  void Interpreter::ProcessLeaderPartitionId(int argc, char *argv[])
  {
      ThreadError error = kThreadError_None;
      unsigned long value;
  
      if (argc == 0)
      {
          sServer->OutputFormat("%u\r\n", otGetLocalLeaderPartitionId());
      }
      else
      {
          SuccessOrExit(error = ParseUnsignedLong(argv[0], value));
          otSetLocalLeaderPartitionId(static_cast<uint32_t>(value));
      }
  
  exit:
      AppendResult(error);
  }
  
  void Interpreter::ProcessLeaderWeight(int argc, char *argv[])
  {
      ThreadError error = kThreadError_None;
      long value;
  
      if (argc == 0)
      {
          sServer->OutputFormat("%d\r\n", otGetLocalLeaderWeight());
      }
      else
      {
          SuccessOrExit(error = ParseLong(argv[0], value));
          otSetLocalLeaderWeight(static_cast<uint8_t>(value));
      }
  
  exit:
      AppendResult(error);
  }
  
  void Interpreter::ProcessLinkQuality(int argc, char *argv[])
  {
      ThreadError error = kThreadError_None;
      uint8_t extAddress[8];
      uint8_t linkQuality;
      long value;
  
      VerifyOrExit(Hex2Bin(argv[0], extAddress, OT_EXT_ADDRESS_SIZE) >= 0, error = kThreadError_Parse);
  
      if (argc == 1)
      {
          VerifyOrExit(otGetAssignLinkQuality(extAddress, &linkQuality) == kThreadError_None,
                       error = kThreadError_InvalidArgs);
          sServer->OutputFormat("%d\r\n", linkQuality);
      }
      else
      {
          SuccessOrExit(error = ParseLong(argv[1], value));
          otSetAssignLinkQuality(extAddress, static_cast<uint8_t>(value));
      }
  
  exit:
      AppendResult(error);
  }
  
  void Interpreter::ProcessMasterKey(int argc, char *argv[])
  {
      ThreadError error = kThreadError_None;
  
      if (argc == 0)
      {
          uint8_t keyLength;
          const uint8_t *key = otGetMasterKey(&keyLength);
  
          for (int i = 0; i < keyLength; i++)
          {
              sServer->OutputFormat("%02x", key[i]);
          }
  
          sServer->OutputFormat("\r\n");
      }
      else
      {
          int keyLength;
          uint8_t key[OT_MASTER_KEY_SIZE];
  
          VerifyOrExit((keyLength = Hex2Bin(argv[0], key, sizeof(key))) == OT_MASTER_KEY_SIZE, error = kThreadError_Parse);
          SuccessOrExit(error = otSetMasterKey(key, static_cast<uint8_t>(keyLength)));
      }
  
  exit:
      AppendResult(error);
  }
  
  void Interpreter::ProcessMode(int argc, char *argv[])
  {
      ThreadError error = kThreadError_None;
      otLinkModeConfig linkMode;
  
      memset(&linkMode, 0, sizeof(otLinkModeConfig));
  
      if (argc == 0)
      {
          linkMode = otGetLinkMode();
  
          if (linkMode.mRxOnWhenIdle)
          {
              sServer->OutputFormat("r");
          }
  
          if (linkMode.mSecureDataRequests)
          {
              sServer->OutputFormat("s");
          }
  
          if (linkMode.mDeviceType)
          {
              sServer->OutputFormat("d");
          }
  
          if (linkMode.mNetworkData)
          {
              sServer->OutputFormat("n");
          }
  
          sServer->OutputFormat("\r\n");
      }
      else
      {
          for (char *arg = argv[0]; *arg != '\0'; arg++)
          {
              switch (*arg)
              {
              case 'r':
                  linkMode.mRxOnWhenIdle = 1;
                  break;
  
              case 's':
                  linkMode.mSecureDataRequests = 1;
                  break;
  
              case 'd':
                  linkMode.mDeviceType = 1;
                  break;
  
              case 'n':
                  linkMode.mNetworkData = 1;
                  break;
  
              default:
                  ExitNow(error = kThreadError_Parse);
              }
          }
  
          SuccessOrExit(error = otSetLinkMode(linkMode));
      }
  
  exit:
      AppendResult(error);
  }
  
  void Interpreter::ProcessNetworkDataRegister(int argc, char *argv[])
  {
      ThreadError error = kThreadError_None;
      SuccessOrExit(error = otSendServerData());
  
  exit:
      (void)argc;
      (void)argv;
      AppendResult(error);
  }
  
  void Interpreter::ProcessNetworkIdTimeout(int argc, char *argv[])
  {
      ThreadError error = kThreadError_None;
      long value;
  
      if (argc == 0)
      {
          sServer->OutputFormat("%d\r\n", otGetNetworkIdTimeout());
      }
      else
      {
          SuccessOrExit(error = ParseLong(argv[0], value));
          otSetNetworkIdTimeout(static_cast<uint8_t>(value));
      }
  
  exit:
      AppendResult(error);
  }
  
  void Interpreter::ProcessNetworkName(int argc, char *argv[])
  {
      ThreadError error = kThreadError_None;
  
      if (argc == 0)
      {
          sServer->OutputFormat("%.*s\r\n", OT_NETWORK_NAME_MAX_SIZE, otGetNetworkName());
      }
      else
      {
          SuccessOrExit(error = otSetNetworkName(argv[0]));
      }
  
  exit:
      AppendResult(error);
  }
  
  void Interpreter::ProcessPanId(int argc, char *argv[])
  {
      ThreadError error = kThreadError_None;
      long value;
  
      if (argc == 0)
      {
          sServer->OutputFormat("%d\r\n", otGetPanId());
      }
      else
      {
          SuccessOrExit(error = ParseLong(argv[0], value));
          otSetPanId(static_cast<otPanId>(value));
      }
  
  exit:
      AppendResult(error);
  }
  
  void Interpreter::ProcessParent(int argc, char *argv[])
  {
      ThreadError error = kThreadError_None;
      otRouterInfo parentInfo;
  
      SuccessOrExit(error = otGetParentInfo(&parentInfo));
      sServer->OutputFormat("Ext Addr: ");
  
      for (size_t i = 0; i < sizeof(parentInfo.mExtAddress); i++)
      {
          sServer->OutputFormat("%02x", parentInfo.mExtAddress.m8[i]);
      }
  
      sServer->OutputFormat("\r\n");
  
      sServer->OutputFormat("Rloc: %x\r\n", parentInfo.mRloc16);
  
  exit:
      (void)argc;
      (void)argv;
      AppendResult(error);
  }
  
  void Interpreter::HandleEchoResponse(void *aContext, Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
  {
      Ip6::IcmpHeader icmp6Header;
      uint32_t timestamp = 0;
  
      aMessage.Read(aMessage.GetOffset(), sizeof(icmp6Header), &icmp6Header);
  
      sServer->OutputFormat("%d bytes from ", aMessage.GetLength() - aMessage.GetOffset());
      sServer->OutputFormat("%x:%x:%x:%x:%x:%x:%x:%x",
                            HostSwap16(aMessageInfo.GetPeerAddr().mFields.m16[0]),
                            HostSwap16(aMessageInfo.GetPeerAddr().mFields.m16[1]),
                            HostSwap16(aMessageInfo.GetPeerAddr().mFields.m16[2]),
                            HostSwap16(aMessageInfo.GetPeerAddr().mFields.m16[3]),
                            HostSwap16(aMessageInfo.GetPeerAddr().mFields.m16[4]),
                            HostSwap16(aMessageInfo.GetPeerAddr().mFields.m16[5]),
                            HostSwap16(aMessageInfo.GetPeerAddr().mFields.m16[6]),
                            HostSwap16(aMessageInfo.GetPeerAddr().mFields.m16[7]));
      sServer->OutputFormat(": icmp_seq=%d hlim=%d", icmp6Header.GetSequence(), aMessageInfo.mHopLimit);
  
      if (aMessage.Read(aMessage.GetOffset() + sizeof(icmp6Header), sizeof(uint32_t), &timestamp) >=
          static_cast<int>(sizeof(uint32_t)))
      {
          sServer->OutputFormat(" time=%dms", Timer::GetNow() - HostSwap32(timestamp));
      }
  
      sServer->OutputFormat("\r\n");
  
      (void)aContext;
  }
  
  void Interpreter::ProcessPing(int argc, char *argv[])
  {
      ThreadError error = kThreadError_None;
      uint8_t index = 1;
      long value;
  
      VerifyOrExit(argc > 0, error = kThreadError_Parse);
      VerifyOrExit(!sPingTimer->IsRunning(), error = kThreadError_Busy);
  
      memset(&sMessageInfo, 0, sizeof(sMessageInfo));
      SuccessOrExit(error = sMessageInfo.GetPeerAddr().FromString(argv[0]));
      sMessageInfo.mInterfaceId = 1;
  
      sLength = 8;
      sCount = 1;
      sInterval = 1000;
  
      while (index < argc)
      {
          SuccessOrExit(error = ParseLong(argv[index], value));
  
          switch (index)
          {
          case 1:
              sLength = (uint16_t)value;
              break;
  
          case 2:
              sCount = (uint16_t)value;
              break;
  
          case 3:
              sInterval = (uint32_t)value;
              sInterval = sInterval * 1000;
              break;
  
          default:
              ExitNow(error = kThreadError_Parse);
          }
  
          index++;
      }
  
      HandlePingTimer(NULL);
  
      return;
  
  exit:
      AppendResult(error);
  }
  
  void Interpreter::HandlePingTimer(void *aContext)
  {
      ThreadError error = kThreadError_None;
      uint32_t timestamp = HostSwap32(Timer::GetNow());
      Message *message;
  
      VerifyOrExit((message = Ip6::Icmp::NewMessage(0)) != NULL, error = kThreadError_NoBufs);
      SuccessOrExit(error = message->Append(&timestamp, sizeof(timestamp)));
      SuccessOrExit(error = message->SetLength(sLength));
  
      SuccessOrExit(error = Ip6::Icmp::SendEchoRequest(*message, sMessageInfo));
      sCount--;
  
  exit:
  
      if (error != kThreadError_None && message != NULL)
      {
          Message::Free(*message);
      }
  
      if (sCount)
      {
          sPingTimer->Start(sInterval);
      }
  
      (void)aContext;
  }
  
  void Interpreter::ProcessPollPeriod(int argc, char *argv[])
  {
      ThreadError error = kThreadError_None;
      long value;
  
      if (argc == 0)
      {
          sServer->OutputFormat("%d\r\n", (otGetPollPeriod() / 1000));  // ms->s
      }
      else
      {
          SuccessOrExit(error = ParseLong(argv[0], value));
          otSetPollPeriod(static_cast<uint32_t>(value * 1000));  // s->ms
      }
  
  exit:
      AppendResult(error);
  }
  
  void Interpreter::ProcessPromiscuous(int argc, char *argv[])
  {
      ThreadError error = kThreadError_None;
  
      if (argc == 0)
      {
          if (otIsLinkPromiscuous() && otPlatRadioGetPromiscuous())
          {
              sServer->OutputFormat("Enabled\r\n");
          }
          else
          {
              sServer->OutputFormat("Disabled\r\n");
          }
      }
      else
      {
          if (strcmp(argv[0], "enable") == 0)
          {
              otSetLinkPcapCallback(&HandleLinkPcapReceive, NULL);
              SuccessOrExit(error = otSetLinkPromiscuous(true));
          }
          else if (strcmp(argv[0], "disable") == 0)
          {
              otSetLinkPcapCallback(NULL, NULL);
              SuccessOrExit(error = otSetLinkPromiscuous(false));
          }
      }
  
  exit:
      AppendResult(error);
  }
  
  void Interpreter::HandleLinkPcapReceive(const RadioPacket *aFrame, void *aContext)
  {
      sServer->OutputFormat("\r\n");
  
      for (size_t i = 0; i < 44; i++)
      {
          sServer->OutputFormat("=");
      }
  
      sServer->OutputFormat("[len = %3u]", aFrame->mLength);
  
      for (size_t i = 0; i < 28; i++)
      {
          sServer->OutputFormat("=");
      }
  
      sServer->OutputFormat("\r\n");
  
      for (size_t i = 0; i < aFrame->mLength; i += 16)
      {
          sServer->OutputFormat("|");
  
          for (size_t j = 0; j < 16; j++)
          {
              if (i + j < aFrame->mLength)
              {
                  sServer->OutputFormat(" %02X", aFrame->mPsdu[i + j]);
              }
              else
              {
                  sServer->OutputFormat(" ..");
              }
          }
  
          sServer->OutputFormat("|");
  
          for (size_t j = 0; j < 16; j++)
          {
              if (i + j < aFrame->mLength)
              {
                  if (31 < aFrame->mPsdu[i + j] && aFrame->mPsdu[i + j] < 127)
                  {
                      sServer->OutputFormat(" %c", aFrame->mPsdu[i + j]);
                  }
                  else
                  {
                      sServer->OutputFormat(" ?");
                  }
              }
              else
              {
                  sServer->OutputFormat(" .");
              }
          }
  
          sServer->OutputFormat("|\r\n");
      }
  
      for (size_t i = 0; i < 83; i++)
      {
          sServer->OutputFormat("-");
      }
  
      sServer->OutputFormat("\r\n");
  
      (void) aContext;
  }
  
  ThreadError Interpreter::ProcessPrefixAdd(int argc, char *argv[])
  {
      ThreadError error = kThreadError_None;
      otBorderRouterConfig config;
      int argcur = 0;
  
      memset(&config, 0, sizeof(otBorderRouterConfig));
  
      char *prefixLengthStr;
      char *endptr;
  
      if ((prefixLengthStr = strchr(argv[argcur], '/')) == NULL)
      {
          ExitNow();
      }
  
      *prefixLengthStr++ = '\0';
  
      SuccessOrExit(error = otIp6AddressFromString(argv[argcur], &config.mPrefix.mPrefix));
  
      config.mPrefix.mLength = static_cast<uint8_t>(strtol(prefixLengthStr, &endptr, 0));
  
      if (*endptr != '\0')
      {
          ExitNow(error = kThreadError_Parse);
      }
  
      argcur++;
  
      for (; argcur < argc; argcur++)
      {
          if (strcmp(argv[argcur], "high") == 0)
          {
              config.mPreference = 1;
          }
          else if (strcmp(argv[argcur], "med") == 0)
          {
              config.mPreference = 0;
          }
          else if (strcmp(argv[argcur], "low") == 0)
          {
              config.mPreference = -1;
          }
          else
          {
              for (char *arg = argv[argcur]; *arg != '\0'; arg++)
              {
                  switch (*arg)
                  {
                  case 'p':
                      config.mPreferred = true;
                      break;
  
                  case 'a':
                      config.mSlaac = true;
                      break;
  
                  case 'd':
                      config.mDhcp = true;
                      break;
  
                  case 'c':
                      config.mConfigure = true;
                      break;
  
                  case 'r':
                      config.mDefaultRoute = true;
                      break;
  
                  case 'o':
                      config.mOnMesh = true;
                      break;
  
                  case 's':
                      config.mStable = true;
                      break;
  
                  default:
                      ExitNow(error = kThreadError_Parse);
                  }
              }
          }
      }
  
      error = otAddBorderRouter(&config);
  
  exit:
      return error;
  }
  
  ThreadError Interpreter::ProcessPrefixRemove(int argc, char *argv[])
  {
      ThreadError error = kThreadError_None;
      struct otIp6Prefix prefix;
      int argcur = 0;
  
      memset(&prefix, 0, sizeof(otIp6Prefix));
  
      char *prefixLengthStr;
      char *endptr;
  
      if ((prefixLengthStr = strchr(argv[argcur], '/')) == NULL)
      {
          ExitNow();
      }
  
      *prefixLengthStr++ = '\0';
  
      SuccessOrExit(error = otIp6AddressFromString(argv[argcur], &prefix.mPrefix));
  
      prefix.mLength = static_cast<uint8_t>(strtol(prefixLengthStr, &endptr, 0));
  
      if (*endptr != '\0')
      {
          ExitNow(error = kThreadError_Parse);
      }
  
      error = otRemoveBorderRouter(&prefix);
  
  exit:
      (void)argc;
      return error;
  }
  
  ThreadError Interpreter::ProcessPrefixList(void)
  {
      otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT;
      otBorderRouterConfig config;
  
      while (otGetNextOnMeshPrefix(true, &iterator, &config) == kThreadError_None)
      {
          sServer->OutputFormat("%x:%x:%x:%x::/%d ",
                                HostSwap16(config.mPrefix.mPrefix.mFields.m16[0]),
                                HostSwap16(config.mPrefix.mPrefix.mFields.m16[1]),
                                HostSwap16(config.mPrefix.mPrefix.mFields.m16[2]),
                                HostSwap16(config.mPrefix.mPrefix.mFields.m16[3]),
                                config.mPrefix.mLength);
  
          if (config.mPreferred)
          {
              sServer->OutputFormat("p");
          }
  
          if (config.mSlaac)
          {
              sServer->OutputFormat("a");
          }
  
          if (config.mDhcp)
          {
              sServer->OutputFormat("d");
          }
  
          if (config.mConfigure)
          {
              sServer->OutputFormat("c");
          }
  
          if (config.mDefaultRoute)
          {
              sServer->OutputFormat("r");
          }
  
          if (config.mOnMesh)
          {
              sServer->OutputFormat("o");
          }
  
          if (config.mStable)
          {
              sServer->OutputFormat("s");
          }
  
          switch (config.mPreference)
          {
          case -1:
              sServer->OutputFormat(" low\r\n");
              break;
  
          case 0:
              sServer->OutputFormat(" med\r\n");
              break;
  
          case 1:
              sServer->OutputFormat(" high\r\n");
              break;
          }
      }
  
      return kThreadError_None;
  }
  
  void Interpreter::ProcessPrefix(int argc, char *argv[])
  {
      ThreadError error = kThreadError_None;
  
      if (argc == 0)
      {
          SuccessOrExit(error = ProcessPrefixList());
      }
      else if (strcmp(argv[0], "add") == 0)
      {
          SuccessOrExit(error = ProcessPrefixAdd(argc - 1, argv + 1));
      }
      else if (strcmp(argv[0], "remove") == 0)
      {
          SuccessOrExit(error = ProcessPrefixRemove(argc - 1, argv + 1));
      }
      else
      {
          ExitNow(error = kThreadError_Parse);
      }
  
  exit:
      AppendResult(error);
  }
  
  void Interpreter::ProcessReleaseRouterId(int argc, char *argv[])
  {
      ThreadError error = kThreadError_None;
      long value;
  
      VerifyOrExit(argc > 0, error = kThreadError_Parse);
  
      SuccessOrExit(error = ParseLong(argv[0], value));
      SuccessOrExit(error = otReleaseRouterId(static_cast<uint8_t>(value)));
  
  exit:
      AppendResult(error);
  }
  
  void Interpreter::ProcessReset(int argc, char *argv[])
  {
      otPlatformReset();
      (void)argc;
      (void)argv;
  }
  
  void Interpreter::ProcessRloc16(int argc, char *argv[])
  {
      sServer->OutputFormat("%04x\r\n", otGetRloc16());
      sServer->OutputFormat("Done\r\n");
      (void)argc;
      (void)argv;
  }
  
  ThreadError Interpreter::ProcessRouteAdd(int argc, char *argv[])
  {
      ThreadError error = kThreadError_None;
      otExternalRouteConfig config;
      int argcur = 0;
  
      memset(&config, 0, sizeof(otExternalRouteConfig));
  
      char *prefixLengthStr;
      char *endptr;
  
      VerifyOrExit(argc > 0, error = kThreadError_Parse);
  
      if ((prefixLengthStr = strchr(argv[argcur], '/')) == NULL)
      {
          ExitNow();
      }
  
      *prefixLengthStr++ = '\0';
  
      SuccessOrExit(error = otIp6AddressFromString(argv[argcur], &config.mPrefix.mPrefix));
  
      config.mPrefix.mLength = static_cast<uint8_t>(strtol(prefixLengthStr, &endptr, 0));
  
      if (*endptr != '\0')
      {
          ExitNow(error = kThreadError_Parse);
      }
  
      argcur++;
  
      for (; argcur < argc; argcur++)
      {
          if (strcmp(argv[argcur], "s") == 0)
          {
              config.mStable = true;
          }
          else if (strcmp(argv[argcur], "high") == 0)
          {
              config.mPreference = 1;
          }
          else if (strcmp(argv[argcur], "med") == 0)
          {
              config.mPreference = 0;
          }
          else if (strcmp(argv[argcur], "low") == 0)
          {
              config.mPreference = -1;
          }
          else
          {
              ExitNow(error = kThreadError_Parse);
          }
      }
  
      error = otAddExternalRoute(&config);
  
  exit:
      return error;
  }
  
  ThreadError Interpreter::ProcessRouteRemove(int argc, char *argv[])
  {
      ThreadError error = kThreadError_None;
      struct otIp6Prefix prefix;
      int argcur = 0;
  
      memset(&prefix, 0, sizeof(struct otIp6Prefix));
      char *prefixLengthStr;
      char *endptr;
  
      VerifyOrExit(argc > 0, error = kThreadError_Parse);
  
      if ((prefixLengthStr = strchr(argv[argcur], '/')) == NULL)
      {
          ExitNow();
      }
  
      *prefixLengthStr++ = '\0';
  
      SuccessOrExit(error = otIp6AddressFromString(argv[argcur], &prefix.mPrefix));
  
      prefix.mLength = static_cast<uint8_t>(strtol(prefixLengthStr, &endptr, 0));
  
      if (*endptr != '\0')
      {
          ExitNow(error = kThreadError_Parse);
      }
  
      error = otRemoveExternalRoute(&prefix);
  
  exit:
      return error;
  }
  
  void Interpreter::ProcessRoute(int argc, char *argv[])
  {
      ThreadError error = kThreadError_None;
  
      VerifyOrExit(argc > 0, error = kThreadError_Parse);
  
      if (strcmp(argv[0], "add") == 0)
      {
          SuccessOrExit(error = ProcessRouteAdd(argc - 1, argv + 1));
      }
      else if (strcmp(argv[0], "remove") == 0)
      {
          SuccessOrExit(error = ProcessRouteRemove(argc - 1, argv + 1));
      }
      else
      {
          ExitNow(error = kThreadError_Parse);
      }
  
  exit:
      AppendResult(error);
  }
  
  void Interpreter::ProcessRouter(int argc, char *argv[])
  {
      ThreadError error = kThreadError_None;
      otRouterInfo routerInfo;
      long value;
  
      VerifyOrExit(argc > 0, error = kThreadError_Parse);
  
      if (strcmp(argv[0], "list") == 0)
      {
          for (uint8_t i = 0; ; i++)
          {
              if (otGetRouterInfo(i, &routerInfo) != kThreadError_None)
              {
                  sServer->OutputFormat("\r\n");
                  ExitNow();
              }
  
              if (routerInfo.mAllocated)
              {
                  sServer->OutputFormat("%d ", i);
              }
          }
      }
  
      SuccessOrExit(error = ParseLong(argv[0], value));
      SuccessOrExit(error = otGetRouterInfo(static_cast<uint16_t>(value), &routerInfo));
  
      sServer->OutputFormat("Alloc: %d\r\n", routerInfo.mAllocated);
  
      if (routerInfo.mAllocated)
      {
          sServer->OutputFormat("Router ID: %d\r\n", routerInfo.mRouterId);
          sServer->OutputFormat("Rloc: %04x\r\n", routerInfo.mRloc16);
          sServer->OutputFormat("Next Hop: %04x\r\n", static_cast<uint16_t>(routerInfo.mNextHop) << 10);
          sServer->OutputFormat("Link: %d\r\n", routerInfo.mLinkEstablished);
  
          if (routerInfo.mLinkEstablished)
          {
              sServer->OutputFormat("Ext Addr: ");
  
              for (size_t j = 0; j < sizeof(routerInfo.mExtAddress); j++)
              {
                  sServer->OutputFormat("%02x", routerInfo.mExtAddress.m8[j]);
              }
  
              sServer->OutputFormat("\r\n");
              sServer->OutputFormat("Cost: %d\r\n", routerInfo.mPathCost);
              sServer->OutputFormat("LQI In: %d\r\n", routerInfo.mLinkQualityIn);
              sServer->OutputFormat("LQI Out: %d\r\n", routerInfo.mLinkQualityOut);
              sServer->OutputFormat("Age: %d\r\n", routerInfo.mAge);
          }
      }
  
  exit:
      AppendResult(error);
  }
  
  void Interpreter::ProcessRouterRole(int argc, char *argv[])
  {
      ThreadError error = kThreadError_None;
  
      if (argc == 0)
      {
          if (otIsRouterRoleEnabled())
          {
              sServer->OutputFormat("Enabled\r\n");
          }
          else
          {
              sServer->OutputFormat("Disabled\r\n");
          }
      }
      else if (strcmp(argv[0], "enable") == 0)
      {
          otSetRouterRoleEnabled(true);
      }
      else if (strcmp(argv[0], "disable") == 0)
      {
          otSetRouterRoleEnabled(false);
      }
      else
      {
          ExitNow(error = kThreadError_Parse);
      }
  
  exit:
      AppendResult(error);
  }
  
  void Interpreter::ProcessRouterUpgradeThreshold(int argc, char *argv[])
  {
      ThreadError error = kThreadError_None;
      long value;
  
      if (argc == 0)
      {
          sServer->OutputFormat("%d\r\n", otGetRouterUpgradeThreshold());
      }
      else
      {
          SuccessOrExit(error = ParseLong(argv[0], value));
          otSetRouterUpgradeThreshold(static_cast<uint8_t>(value));
      }
  
  exit:
      AppendResult(error);
  }
  
  void Interpreter::ProcessScan(int argc, char *argv[])
  {
      ThreadError error = kThreadError_None;
      uint32_t scanChannels = 0;
      long value;
  
      if (argc > 0)
      {
          SuccessOrExit(error = ParseLong(argv[0], value));
          scanChannels = 1 << value;
      }
  
      SuccessOrExit(error = otActiveScan(scanChannels, 0, &HandleActiveScanResult, NULL));
      sServer->OutputFormat("| J | Network Name     | Extended PAN     | PAN  | MAC Address      | Ch | dBm | LQI |\r\n");
      sServer->OutputFormat("+---+------------------+------------------+------+------------------+----+-----+-----+\r\n");
  
      return;
  
  exit:
      AppendResult(error);
  }
  
  void Interpreter::HandleActiveScanResult(otActiveScanResult *aResult, void *aContext)
  {
      if (aResult == NULL)
      {
          sServer->OutputFormat("Done\r\n");
          ExitNow();
      }
  
      sServer->OutputFormat("| %d ", aResult->mIsJoinable);
  
      sServer->OutputFormat("| %-16s ", aResult->mNetworkName.m8);
  
      sServer->OutputFormat("| ");
      OutputBytes(aResult->mExtendedPanId.m8, OT_EXT_PAN_ID_SIZE);
      sServer->OutputFormat(" ");
  
      sServer->OutputFormat("| %04x | ", aResult->mPanId);
      OutputBytes(aResult->mExtAddress.m8, OT_EXT_ADDRESS_SIZE);
      sServer->OutputFormat(" | %2d ", aResult->mChannel);
      sServer->OutputFormat("| %3d ", aResult->mRssi);
      sServer->OutputFormat("| %3d |\r\n", aResult->mLqi);
  
  exit:
      (void)aContext;
      return;
  }
  
  void Interpreter::ProcessSingleton(int argc, char *argv[])
  {
      ThreadError error = kThreadError_None;
  
      if (otIsSingleton())
      {
          sServer->OutputFormat("true\r\n");
      }
      else
      {
          sServer->OutputFormat("false\r\n");
      }
  
      (void)argc;
      (void)argv;
  
      AppendResult(error);
  }
  
  void Interpreter::ProcessState(int argc, char *argv[])
  {
      ThreadError error = kThreadError_None;
  
      if (argc == 0)
      {
          switch (otGetDeviceRole())
          {
          case kDeviceRoleDisabled:
              sServer->OutputFormat("disabled\r\n");
              break;
  
          case kDeviceRoleDetached:
              sServer->OutputFormat("detached\r\n");
              break;
  
          case kDeviceRoleChild:
              sServer->OutputFormat("child\r\n");
              break;
  
          case kDeviceRoleRouter:
              sServer->OutputFormat("router\r\n");
              break;
  
          case kDeviceRoleLeader:
              sServer->OutputFormat("leader\r\n");
              break;
          }
      }
      else
      {
          if (strcmp(argv[0], "detached") == 0)
          {
              SuccessOrExit(error = otBecomeDetached());
          }
          else if (strcmp(argv[0], "child") == 0)
          {
              SuccessOrExit(error = otBecomeChild(kMleAttachSamePartition));
          }
          else if (strcmp(argv[0], "router") == 0)
          {
              SuccessOrExit(error = otBecomeRouter());
          }
          else if (strcmp(argv[0], "leader") == 0)
          {
              SuccessOrExit(error = otBecomeLeader());
          }
          else
          {
              ExitNow(error = kThreadError_Parse);
          }
      }
  
  exit:
      AppendResult(error);
  }
  
  void Interpreter::ProcessThread(int argc, char *argv[])
  {
      ThreadError error = kThreadError_Parse;
  
      VerifyOrExit(argc > 0, error = kThreadError_Parse);
  
      if (strcmp(argv[0], "start") == 0)
      {
          SuccessOrExit(error = otThreadStart());
      }
      else if (strcmp(argv[0], "stop") == 0)
      {
          SuccessOrExit(error = otThreadStop());
      }
  
  exit:
      (void)argc;
      (void)argv;
      AppendResult(error);
  }
  
  void Interpreter::ProcessVersion(int argc, char *argv[])
  {
      sServer->OutputFormat("%s\r\n", otGetVersionString());
      AppendResult(kThreadError_None);
      (void)argc;
      (void)argv;
  }
  
  void Interpreter::ProcessWhitelist(int argc, char *argv[])
  {
      ThreadError error = kThreadError_None;
      otMacWhitelistEntry entry;
      int argcur = 0;
      uint8_t extAddr[8];
      int8_t rssi;
  
      if (argcur >= argc)
      {
          if (otIsMacWhitelistEnabled())
          {
              sServer->OutputFormat("Enabled\r\n");
          }
          else
          {
              sServer->OutputFormat("Disabled\r\n");
          }
  
          for (uint8_t i = 0; ; i++)
          {
              if (otGetMacWhitelistEntry(i, &entry) != kThreadError_None)
              {
                  break;
              }
  
              if (entry.mValid == false)
              {
                  continue;
              }
  
              OutputBytes(entry.mExtAddress.m8, OT_EXT_ADDRESS_SIZE);
  
              if (entry.mFixedRssi)
              {
                  sServer->OutputFormat(" %d", entry.mRssi);
              }
  
              sServer->OutputFormat("\r\n");
          }
      }
      else if (strcmp(argv[argcur], "add") == 0)
      {
          VerifyOrExit(++argcur < argc, error = kThreadError_Parse);
          VerifyOrExit(Hex2Bin(argv[argcur], extAddr, sizeof(extAddr)) == sizeof(extAddr), error = kThreadError_Parse);
  
          if (++argcur < argc)
          {
              rssi = static_cast<int8_t>(strtol(argv[argcur], NULL, 0));
              VerifyOrExit(otAddMacWhitelistRssi(extAddr, rssi) == kThreadError_None, error = kThreadError_Parse);
          }
          else
          {
              otAddMacWhitelist(extAddr);
              VerifyOrExit(otAddMacWhitelist(extAddr) == kThreadError_None, error = kThreadError_Parse);
          }
      }
      else if (strcmp(argv[argcur], "clear") == 0)
      {
          otClearMacWhitelist();
      }
      else if (strcmp(argv[argcur], "disable") == 0)
      {
          otDisableMacWhitelist();
      }
      else if (strcmp(argv[argcur], "enable") == 0)
      {
          otEnableMacWhitelist();
      }
      else if (strcmp(argv[argcur], "remove") == 0)
      {
          VerifyOrExit(++argcur < argc, error = kThreadError_Parse);
          VerifyOrExit(Hex2Bin(argv[argcur], extAddr, sizeof(extAddr)) == sizeof(extAddr), error = kThreadError_Parse);
          otRemoveMacWhitelist(extAddr);
      }
  
  exit:
      AppendResult(error);
  }
  
  #if OPENTHREAD_ENABLE_DIAG
  void Interpreter::ProcessDiag(int argc, char *argv[])
  {
      // all diagnostics related features are processed within diagnostics module
      sServer->OutputFormat("%s\r\n", diagProcessCmd(argc, argv));
  }
  #endif
  
  void Interpreter::ProcessLine(char *aBuf, uint16_t aBufLength, Server &aServer)
  {
      char *argv[kMaxArgs];
      int argc = 0;
      char *cmd;
  
      sServer = &aServer;
  
      VerifyOrExit(aBuf != NULL, ;);
  
      for (; *aBuf == ' '; aBuf++, aBufLength--);
  
      for (cmd = aBuf + 1; (cmd < aBuf + aBufLength) && (cmd != NULL); ++cmd)
      {
          VerifyOrExit(argc < kMaxArgs,
                       sServer->OutputFormat("Error: too many args (max %d)\r\n", kMaxArgs));
  
          if (*cmd == ' ' || *cmd == '\r' || *cmd == '\n')
          {
              *cmd = '\0';
          }
  
          if (*(cmd - 1) == '\0' && *cmd != ' ')
          {
              argv[argc++] = cmd;
          }
      }
  
      cmd = aBuf;
  
  #if OPENTHREAD_ENABLE_DIAG
      VerifyOrExit((!isDiagEnabled() || (strcmp(cmd, "diag") == 0)),
                   sServer->OutputFormat("under diagnostics mode, execute 'diag stop' before running any other commands.\r\n"));
  #endif
  
      for (unsigned int i = 0; i < sizeof(sCommands) / sizeof(sCommands[0]); i++)
      {
          if (strcmp(cmd, sCommands[i].mName) == 0)
          {
              sCommands[i].mCommand(argc, argv);
              break;
          }
      }
  
  exit:
      return;
  }
  
  void Interpreter::HandleNetifStateChanged(uint32_t aFlags, void *aContext)
  {
      otNetworkDataIterator iterator;
      otBorderRouterConfig config;
  
      VerifyOrExit((aFlags & OT_THREAD_NETDATA_UPDATED) != 0, ;);
  
      // remove addresses
      for (size_t i = 0; i < sizeof(sAutoAddresses) / sizeof(sAutoAddresses[0]); i++)
      {
          otNetifAddress *address = &sAutoAddresses[i];
          bool found = false;
  
          if (address->mValidLifetime == 0)
          {
              continue;
          }
  
          iterator = OT_NETWORK_DATA_ITERATOR_INIT;
  
          while (otGetNextOnMeshPrefix(false, &iterator, &config) == kThreadError_None)
          {
              if (config.mSlaac == false)
              {
                  continue;
              }
  
              if (otIp6PrefixMatch(&config.mPrefix.mPrefix, &address->mAddress) >= config.mPrefix.mLength &&
                  config.mPrefix.mLength == address->mPrefixLength)
              {
                  found = true;
                  break;
              }
          }
  
          if (!found)
          {
              otRemoveUnicastAddress(&address->mAddress);
              address->mValidLifetime = 0;
          }
      }
  
      // add addresses
      iterator = OT_NETWORK_DATA_ITERATOR_INIT;
  
      while (otGetNextOnMeshPrefix(false, &iterator, &config) == kThreadError_None)
      {
          bool found = false;
  
          if (config.mSlaac == false)
          {
              continue;
          }
  
          for (size_t i = 0; i < sizeof(sAutoAddresses) / sizeof(sAutoAddresses[0]); i++)
          {
              otNetifAddress *address = &sAutoAddresses[i];
  
              if (address->mValidLifetime == 0)
              {
                  continue;
              }
  
              if (otIp6PrefixMatch(&config.mPrefix.mPrefix, &address->mAddress) >= config.mPrefix.mLength &&
                  config.mPrefix.mLength == address->mPrefixLength)
              {
                  found = true;
                  break;
              }
          }
  
          if (!found)
          {
              for (size_t i = 0; i < sizeof(sAutoAddresses) / sizeof(sAutoAddresses[0]); i++)
              {
                  otNetifAddress *address = &sAutoAddresses[i];
  
                  if (address->mValidLifetime != 0)
                  {
                      continue;
                  }
  
                  memset(address, 0, sizeof(*address));
                  memcpy(&address->mAddress, &config.mPrefix.mPrefix, 8);
  
                  for (size_t j = 8; j < sizeof(address->mAddress); j++)
                  {
                      address->mAddress.mFields.m8[j] = (uint8_t)otPlatRandomGet();
                  }
  
                  address->mPrefixLength = config.mPrefix.mLength;
                  address->mPreferredLifetime = config.mPreferred ? 0xffffffff : 0;
                  address->mValidLifetime = 0xffffffff;
                  otAddUnicastAddress(address);
                  break;
              }
          }
      }
  
  exit:
      (void)aContext;
      return;
  }
  
  }  // namespace Cli
  }  // namespace Thread
  