GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServerCommon::Handle_vFile_pWrite(
    StringExtractorGDBRemote &packet) {
#ifdef _WIN32
  return SendUnimplementedResponse("GDBRemoteCommunicationServerCommon::Handle_"
                                   "vFile_pWrite() unimplemented");
#else
  packet.SetFilePos(::strlen("vFile:pwrite:"));

  StreamGDBRemote response;
  response.PutChar('F');

  int fd = packet.GetU32(UINT32_MAX);
  if (packet.GetChar() == ',') {
    off_t offset = packet.GetU64(UINT32_MAX);
    if (packet.GetChar() == ',') {
      std::string buffer;
      if (packet.GetEscapedBinaryData(buffer)) {
        const ssize_t bytes_written =
            ::pwrite(fd, buffer.data(), buffer.size(), offset);
        const int save_errno = bytes_written == -1 ? errno : 0;
        response.Printf("%zi", bytes_written);
        if (save_errno)
          response.Printf(",%i", save_errno);
      } else {
        response.Printf("-1,%i", EINVAL);
      }
      return SendPacketNoLock(response.GetString());
    }
  }
  return SendErrorResponse(27);
#endif
}
GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServerPlatform::Handle_qQueryGDBServer(
    StringExtractorGDBRemote &packet) {
  if (m_pending_gdb_server.pid == LLDB_INVALID_PROCESS_ID)
    return SendErrorResponse(4);

  JSONObject::SP server_sp = std::make_shared<JSONObject>();
  server_sp->SetObject("port",
                       std::make_shared<JSONNumber>(m_pending_gdb_server.port));
  if (!m_pending_gdb_server.socket_name.empty())
    server_sp->SetObject(
        "socket_name",
        std::make_shared<JSONString>(m_pending_gdb_server.socket_name.c_str()));

  JSONArray server_list;
  server_list.AppendObject(server_sp);

  StreamGDBRemote response;
  server_list.Write(response);

  StreamGDBRemote escaped_response;
  escaped_response.PutEscapedBytes(response.GetString().data(),
                                   response.GetSize());
  return SendPacketNoLock(escaped_response.GetString());
}
GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServerCommon::Handle_qModuleInfo(
    StringExtractorGDBRemote &packet) {
  packet.SetFilePos(::strlen("qModuleInfo:"));

  std::string module_path;
  packet.GetHexByteStringTerminatedBy(module_path, ';');
  if (module_path.empty())
    return SendErrorResponse(1);

  if (packet.GetChar() != ';')
    return SendErrorResponse(2);

  std::string triple;
  packet.GetHexByteString(triple);

  ModuleSpec matched_module_spec = GetModuleInfo(module_path, triple);
  if (!matched_module_spec.GetFileSpec())
    return SendErrorResponse(3);

  const auto file_offset = matched_module_spec.GetObjectOffset();
  const auto file_size = matched_module_spec.GetObjectSize();
  const auto uuid_str = matched_module_spec.GetUUID().GetAsString("");

  StreamGDBRemote response;

  if (uuid_str.empty()) {
    std::string md5_hash;
    if (!FileSystem::CalculateMD5AsString(matched_module_spec.GetFileSpec(),
                                          file_offset, file_size, md5_hash))
      return SendErrorResponse(5);
    response.PutCString("md5:");
    response.PutCStringAsRawHex8(md5_hash.c_str());
  } else {
    response.PutCString("uuid:");
    response.PutCStringAsRawHex8(uuid_str.c_str());
  }
  response.PutChar(';');

  const auto &module_arch = matched_module_spec.GetArchitecture();
  response.PutCString("triple:");
  response.PutCStringAsRawHex8(module_arch.GetTriple().getTriple().c_str());
  response.PutChar(';');

  response.PutCString("file_path:");
  response.PutCStringAsRawHex8(matched_module_spec.GetFileSpec().GetCString());
  response.PutChar(';');
  response.PutCString("file_offset:");
  response.PutHex64(file_offset);
  response.PutChar(';');
  response.PutCString("file_size:");
  response.PutHex64(file_size);
  response.PutChar(';');

  return SendPacketNoLock(response.GetString());
}
GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServerCommon::Handle_vFile_pRead(
    StringExtractorGDBRemote &packet) {
#ifdef _WIN32
  // Not implemented on Windows
  return SendUnimplementedResponse(
      "GDBRemoteCommunicationServerCommon::Handle_vFile_pRead() unimplemented");
#else
  StreamGDBRemote response;
  packet.SetFilePos(::strlen("vFile:pread:"));
  int fd = packet.GetS32(-1);
  if (packet.GetChar() == ',') {
    uint64_t count = packet.GetU64(UINT64_MAX);
    if (packet.GetChar() == ',') {
      uint64_t offset = packet.GetU64(UINT32_MAX);
      if (count == UINT64_MAX) {
        response.Printf("F-1:%i", EINVAL);
        return SendPacketNoLock(response.GetString());
      }

      std::string buffer(count, 0);
      const ssize_t bytes_read = ::pread(fd, &buffer[0], buffer.size(), offset);
      const int save_errno = bytes_read == -1 ? errno : 0;
      response.PutChar('F');
      response.Printf("%zi", bytes_read);
      if (save_errno)
        response.Printf(",%i", save_errno);
      else {
        response.PutChar(';');
        response.PutEscapedBytes(&buffer[0], bytes_read);
      }
      return SendPacketNoLock(response.GetString());
    }
  }
  return SendErrorResponse(21);

#endif
}
GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServerCommon::Handle_qPlatform_mkdir(
    StringExtractorGDBRemote &packet) {
  packet.SetFilePos(::strlen("qPlatform_mkdir:"));
  mode_t mode = packet.GetHexMaxU32(false, UINT32_MAX);
  if (packet.GetChar() == ',') {
    std::string path;
    packet.GetHexByteString(path);
    Error error = FileSystem::MakeDirectory(FileSpec{path, false}, mode);

    StreamGDBRemote response;
    response.Printf("F%u", error.GetError());

    return SendPacketNoLock(response.GetString());
  }
  return SendErrorResponse(20);
}
GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServerCommon::Handle_qSupported(
    StringExtractorGDBRemote &packet) {
  StreamGDBRemote response;

  // Features common to lldb-platform and llgs.
  uint32_t max_packet_size = 128 * 1024; // 128KBytes is a reasonable max packet
                                         // size--debugger can always use less
  response.Printf("PacketSize=%x", max_packet_size);

  response.PutCString(";QStartNoAckMode+");
  response.PutCString(";QThreadSuffixSupported+");
  response.PutCString(";QListThreadsInStopReply+");
  response.PutCString(";qEcho+");
#if defined(__linux__)
  response.PutCString(";qXfer:auxv:read+");
#endif

  return SendPacketNoLock(response.GetString());
}
GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServerCommon::Handle_vFile_MD5(
    StringExtractorGDBRemote &packet) {
  packet.SetFilePos(::strlen("vFile:MD5:"));
  std::string path;
  packet.GetHexByteString(path);
  if (!path.empty()) {
    uint64_t a, b;
    StreamGDBRemote response;
    if (!FileSystem::CalculateMD5(FileSpec(path.c_str(), false), a, b)) {
      response.PutCString("F,");
      response.PutCString("x");
    } else {
      response.PutCString("F,");
      response.PutHex64(a);
      response.PutHex64(b);
    }
    return SendPacketNoLock(response.GetString());
  }
  return SendErrorResponse(25);
}
GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServerCommon::Handle_qPlatform_shell(
    StringExtractorGDBRemote &packet) {
  packet.SetFilePos(::strlen("qPlatform_shell:"));
  std::string path;
  std::string working_dir;
  packet.GetHexByteStringTerminatedBy(path, ',');
  if (!path.empty()) {
    if (packet.GetChar() == ',') {
      // FIXME: add timeout to qPlatform_shell packet
      // uint32_t timeout = packet.GetHexMaxU32(false, 32);
      uint32_t timeout = 10;
      if (packet.GetChar() == ',')
        packet.GetHexByteString(working_dir);
      int status, signo;
      std::string output;
      Error err =
          Host::RunShellCommand(path.c_str(), FileSpec{working_dir, true},
                                &status, &signo, &output, timeout);
      StreamGDBRemote response;
      if (err.Fail()) {
        response.PutCString("F,");
        response.PutHex32(UINT32_MAX);
      } else {
        response.PutCString("F,");
        response.PutHex32(status);
        response.PutChar(',');
        response.PutHex32(signo);
        response.PutChar(',');
        response.PutEscapedBytes(output.c_str(), output.size());
      }
      return SendPacketNoLock(response.GetString());
    }
  }
  return SendErrorResponse(24);
}
GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServerPlatform::Handle_qLaunchGDBServer(
    StringExtractorGDBRemote &packet) {
#ifdef _WIN32
  return SendErrorResponse(9);
#else
  // Spawn a local debugserver as a platform so we can then attach or launch
  // a process...

  Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM));
  if (log)
    log->Printf("GDBRemoteCommunicationServerPlatform::%s() called",
                __FUNCTION__);

  ConnectionFileDescriptor file_conn;
  std::string hostname;
  packet.SetFilePos(::strlen("qLaunchGDBServer;"));
  llvm::StringRef name;
  llvm::StringRef value;
  uint16_t port = UINT16_MAX;
  while (packet.GetNameColonValue(name, value)) {
    if (name.equals("host"))
      hostname = value;
    else if (name.equals("port"))
      value.getAsInteger(0, port);
  }

  lldb::pid_t debugserver_pid = LLDB_INVALID_PROCESS_ID;
  std::string socket_name;
  Status error =
      LaunchGDBServer(Args(), hostname, debugserver_pid, port, socket_name);
  if (error.Fail()) {
    if (log)
      log->Printf("GDBRemoteCommunicationServerPlatform::%s() debugserver "
                  "launch failed: %s",
                  __FUNCTION__, error.AsCString());
    return SendErrorResponse(9);
  }

  if (log)
    log->Printf("GDBRemoteCommunicationServerPlatform::%s() debugserver "
                "launched successfully as pid %" PRIu64,
                __FUNCTION__, debugserver_pid);

  StreamGDBRemote response;
  response.Printf("pid:%" PRIu64 ";port:%u;", debugserver_pid,
                  port + m_port_offset);
  if (!socket_name.empty()) {
    response.PutCString("socket_name:");
    response.PutCStringAsRawHex8(socket_name.c_str());
    response.PutChar(';');
  }

  PacketResult packet_result = SendPacketNoLock(response.GetString());
  if (packet_result != PacketResult::Success) {
    if (debugserver_pid != LLDB_INVALID_PROCESS_ID)
      ::kill(debugserver_pid, SIGINT);
  }
  return packet_result;
#endif
}