<commit_before>// Copyright (c) 2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/process_util.h"

#include <ctype.h>
#include <dirent.h>
#include <fcntl.h>
#include <unistd.h>
#include <string>
#include <sys/types.h>
#include <sys/wait.h>

#include "base/file_util.h"
#include "base/logging.h"
#include "base/string_tokenizer.h"
#include "base/string_util.h"

namespace {

enum ParsingState {
  KEY_NAME,
  KEY_VALUE
};

}  // namespace

namespace base {

bool LaunchApp(const std::vector<std::string>& argv,
               const file_handle_mapping_vector& fds_to_remap,
               bool wait, ProcessHandle* process_handle) {
  bool retval = true;

  char* argv_copy[argv.size() + 1];
  for (size_t i = 0; i < argv.size(); i++) {
    argv_copy[i] = new char[argv[i].size() + 1];
    strcpy(argv_copy[i], argv[i].c_str());
  }
  argv_copy[argv.size()] = NULL;

  // Make sure we don't leak any FDs to the child process by marking all FDs
  // as close-on-exec.
  SetAllFDsToCloseOnExec();

  int pid = fork();
  if (pid == 0) {
    for (file_handle_mapping_vector::const_iterator it = fds_to_remap.begin();
         it != fds_to_remap.end();
         ++it) {
      int src_fd = it->first;
      int dest_fd = it->second;
      if (src_fd == dest_fd) {
        int flags = fcntl(src_fd, F_GETFD);
        if (flags != -1) {
          fcntl(src_fd, F_SETFD, flags & ~FD_CLOEXEC);
        }
      } else {
        dup2(src_fd, dest_fd);
      }
    }

    execvp(argv_copy[0], argv_copy);
  } else if (pid < 0) {
    retval = false;
  } else {
    if (wait)
      waitpid(pid, 0, 0);

    if (process_handle)
      *process_handle = pid;
  }

  for (size_t i = 0; i < argv.size(); i++)
    delete[] argv_copy[i];

  return retval;
}

bool LaunchApp(const CommandLine& cl,
               bool wait, bool start_hidden,
               ProcessHandle* process_handle) {
  file_handle_mapping_vector no_files;
  return LaunchApp(cl.argv(), no_files, wait, process_handle);
}

NamedProcessIterator::NamedProcessIterator(const std::wstring& executable_name,
                                           const ProcessFilter* filter)
    : executable_name_(executable_name), filter_(filter) {
  procfs_dir_ = opendir("/proc");
}

NamedProcessIterator::~NamedProcessIterator() {
  if (procfs_dir_) {
    closedir(procfs_dir_);
    procfs_dir_ = NULL;
  }
}

const ProcessEntry* NamedProcessIterator::NextProcessEntry() {
  bool result = false;
  do {
    result = CheckForNextProcess();
  } while (result && !IncludeEntry());

  if (result)
    return &entry_;

  return NULL;
}

bool NamedProcessIterator::CheckForNextProcess() {
  // TODO(port): skip processes owned by different UID

  dirent* slot = 0;
  const char* openparen;
  const char* closeparen;

  // Arbitrarily guess that there will never be more than 200 non-process
  // files in /proc.  Hardy has 53.
  int skipped = 0;
  const int kSkipLimit = 200;
  while (skipped < kSkipLimit) {
    slot = readdir(procfs_dir_);
    // all done looking through /proc?
    if (!slot)
      return false;

    // If not a process, keep looking for one.
    bool notprocess = false;
    int i;
    for (i = 0; i < NAME_MAX && slot->d_name[i]; ++i) {
       if (!isdigit(slot->d_name[i])) {
         notprocess = true;
         break;
       }
    }
    if (i == NAME_MAX || notprocess) {
      skipped++;
      continue;
    }

    // Read the process's status.
    char buf[NAME_MAX + 12];
    sprintf(buf, "/proc/%s/stat", slot->d_name);
    FILE *fp = fopen(buf, "r");
    if (!fp)
      return false;
    const char* result = fgets(buf, sizeof(buf), fp);
    fclose(fp);
    if (!result)
      return false;

    // Parse the status.  It is formatted like this:
    // %d (%s) %c %d ...
    // pid (name) runstate ppid
    // To avoid being fooled by names containing a closing paren, scan
    // backwards.
    openparen = strchr(buf, '(');
    closeparen = strrchr(buf, ')');
    if (!openparen || !closeparen)
      return false;
    char runstate = closeparen[2];

    // Is the process in 'Zombie' state, i.e. dead but waiting to be reaped?
    // Allowed values: D R S T Z
    if (runstate != 'Z')
      break;

    // Nope, it's a zombie; somebody isn't cleaning up after their children.
    // (e.g. WaitForProcessesToExit doesn't clean up after dead children yet.)
    // There could be a lot of zombies, can't really decrement i here.
  }
  if (skipped >= kSkipLimit) {
    NOTREACHED();
    return false;
  }

  entry_.pid = atoi(slot->d_name);
  entry_.ppid = atoi(closeparen + 3);

  // TODO(port): read pid's commandline's $0, like killall does.  Using the
  // short name between openparen and closeparen won't work for long names!
  int len = closeparen - openparen - 1;
  if (len > NAME_MAX)
    len = NAME_MAX;
  memcpy(entry_.szExeFile, openparen + 1, len);
  entry_.szExeFile[len] = 0;

  return true;
}

bool NamedProcessIterator::IncludeEntry() {
  // TODO(port): make this also work for non-ASCII filenames
  if (WideToASCII(executable_name_) != entry_.szExeFile)
    return false;
  if (!filter_)
    return true;
  return filter_->Includes(entry_.pid, entry_.ppid);
}

// To have /proc/self/io file you must enable CONFIG_TASK_IO_ACCOUNTING
// in your kernel configuration.
bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const {
  std::string proc_io_contents;
  if (!file_util::ReadFileToString(L"/proc/self/io", &proc_io_contents))
    return false;

  (*io_counters).OtherOperationCount = 0;
  (*io_counters).OtherTransferCount = 0;

  StringTokenizer tokenizer(proc_io_contents, ": \n");
  ParsingState state = KEY_NAME;
  std::string last_key_name;
  while (tokenizer.GetNext()) {
    switch (state) {
      case KEY_NAME:
        last_key_name = tokenizer.token();
        state = KEY_VALUE;
        break;
      case KEY_VALUE:
        DCHECK(!last_key_name.empty());
        if (last_key_name == "syscr") {
          (*io_counters).ReadOperationCount = StringToInt64(tokenizer.token());
        } else if (last_key_name == "syscw") {
          (*io_counters).WriteOperationCount = StringToInt64(tokenizer.token());
        } else if (last_key_name == "rchar") {
          (*io_counters).ReadTransferCount = StringToInt64(tokenizer.token());
        } else if (last_key_name == "wchar") {
          (*io_counters).WriteTransferCount = StringToInt64(tokenizer.token());
        }
        state = KEY_NAME;
        break;
    }
  }
  return true;
}

}  // namespace base
<commit_msg>Cleanup in LaunchApp:<commit_after>// Copyright (c) 2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/process_util.h"

#include <ctype.h>
#include <dirent.h>
#include <fcntl.h>
#include <unistd.h>
#include <string>
#include <sys/types.h>
#include <sys/wait.h>

#include "base/file_util.h"
#include "base/logging.h"
#include "base/string_tokenizer.h"
#include "base/string_util.h"

namespace {

enum ParsingState {
  KEY_NAME,
  KEY_VALUE
};

}  // namespace

namespace base {

bool LaunchApp(const std::vector<std::string>& argv,
               const file_handle_mapping_vector& fds_to_remap,
               bool wait, ProcessHandle* process_handle) {
  // Make sure we don't leak any FDs to the child process by marking all FDs
  // as close-on-exec.
  SetAllFDsToCloseOnExec();

  pid_t pid = fork();
  if (pid < 0)
    return false;

  if (pid == 0) {
    for (file_handle_mapping_vector::const_iterator it = fds_to_remap.begin();
         it != fds_to_remap.end();
         ++it) {
      int src_fd = it->first;
      int dest_fd = it->second;
      if (src_fd == dest_fd) {
        int flags = fcntl(src_fd, F_GETFD);
        if (flags != -1) {
          fcntl(src_fd, F_SETFD, flags & ~FD_CLOEXEC);
        }
      } else {
        dup2(src_fd, dest_fd);
      }
    }

    char* argv_cstr[argv.size() + 1];
    for (size_t i = 0; i < argv.size(); i++)
      argv_cstr[i] = const_cast<char*>(argv[i].c_str());
    argv_cstr[argv.size()] = NULL;
    execvp(argv_cstr[0], argv_cstr);
    exit(127);
  } else {
    if (wait)
      waitpid(pid, 0, 0);

    if (process_handle)
      *process_handle = pid;
  }

  return true;
}

bool LaunchApp(const CommandLine& cl,
               bool wait, bool start_hidden,
               ProcessHandle* process_handle) {
  file_handle_mapping_vector no_files;
  return LaunchApp(cl.argv(), no_files, wait, process_handle);
}

NamedProcessIterator::NamedProcessIterator(const std::wstring& executable_name,
                                           const ProcessFilter* filter)
    : executable_name_(executable_name), filter_(filter) {
  procfs_dir_ = opendir("/proc");
}

NamedProcessIterator::~NamedProcessIterator() {
  if (procfs_dir_) {
    closedir(procfs_dir_);
    procfs_dir_ = NULL;
  }
}

const ProcessEntry* NamedProcessIterator::NextProcessEntry() {
  bool result = false;
  do {
    result = CheckForNextProcess();
  } while (result && !IncludeEntry());

  if (result)
    return &entry_;

  return NULL;
}

bool NamedProcessIterator::CheckForNextProcess() {
  // TODO(port): skip processes owned by different UID

  dirent* slot = 0;
  const char* openparen;
  const char* closeparen;

  // Arbitrarily guess that there will never be more than 200 non-process
  // files in /proc.  Hardy has 53.
  int skipped = 0;
  const int kSkipLimit = 200;
  while (skipped < kSkipLimit) {
    slot = readdir(procfs_dir_);
    // all done looking through /proc?
    if (!slot)
      return false;

    // If not a process, keep looking for one.
    bool notprocess = false;
    int i;
    for (i = 0; i < NAME_MAX && slot->d_name[i]; ++i) {
       if (!isdigit(slot->d_name[i])) {
         notprocess = true;
         break;
       }
    }
    if (i == NAME_MAX || notprocess) {
      skipped++;
      continue;
    }

    // Read the process's status.
    char buf[NAME_MAX + 12];
    sprintf(buf, "/proc/%s/stat", slot->d_name);
    FILE *fp = fopen(buf, "r");
    if (!fp)
      return false;
    const char* result = fgets(buf, sizeof(buf), fp);
    fclose(fp);
    if (!result)
      return false;

    // Parse the status.  It is formatted like this:
    // %d (%s) %c %d ...
    // pid (name) runstate ppid
    // To avoid being fooled by names containing a closing paren, scan
    // backwards.
    openparen = strchr(buf, '(');
    closeparen = strrchr(buf, ')');
    if (!openparen || !closeparen)
      return false;
    char runstate = closeparen[2];

    // Is the process in 'Zombie' state, i.e. dead but waiting to be reaped?
    // Allowed values: D R S T Z
    if (runstate != 'Z')
      break;

    // Nope, it's a zombie; somebody isn't cleaning up after their children.
    // (e.g. WaitForProcessesToExit doesn't clean up after dead children yet.)
    // There could be a lot of zombies, can't really decrement i here.
  }
  if (skipped >= kSkipLimit) {
    NOTREACHED();
    return false;
  }

  entry_.pid = atoi(slot->d_name);
  entry_.ppid = atoi(closeparen + 3);

  // TODO(port): read pid's commandline's $0, like killall does.  Using the
  // short name between openparen and closeparen won't work for long names!
  int len = closeparen - openparen - 1;
  if (len > NAME_MAX)
    len = NAME_MAX;
  memcpy(entry_.szExeFile, openparen + 1, len);
  entry_.szExeFile[len] = 0;

  return true;
}

bool NamedProcessIterator::IncludeEntry() {
  // TODO(port): make this also work for non-ASCII filenames
  if (WideToASCII(executable_name_) != entry_.szExeFile)
    return false;
  if (!filter_)
    return true;
  return filter_->Includes(entry_.pid, entry_.ppid);
}

// To have /proc/self/io file you must enable CONFIG_TASK_IO_ACCOUNTING
// in your kernel configuration.
bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const {
  std::string proc_io_contents;
  if (!file_util::ReadFileToString(L"/proc/self/io", &proc_io_contents))
    return false;

  (*io_counters).OtherOperationCount = 0;
  (*io_counters).OtherTransferCount = 0;

  StringTokenizer tokenizer(proc_io_contents, ": \n");
  ParsingState state = KEY_NAME;
  std::string last_key_name;
  while (tokenizer.GetNext()) {
    switch (state) {
      case KEY_NAME:
        last_key_name = tokenizer.token();
        state = KEY_VALUE;
        break;
      case KEY_VALUE:
        DCHECK(!last_key_name.empty());
        if (last_key_name == "syscr") {
          (*io_counters).ReadOperationCount = StringToInt64(tokenizer.token());
        } else if (last_key_name == "syscw") {
          (*io_counters).WriteOperationCount = StringToInt64(tokenizer.token());
        } else if (last_key_name == "rchar") {
          (*io_counters).ReadTransferCount = StringToInt64(tokenizer.token());
        } else if (last_key_name == "wchar") {
          (*io_counters).WriteTransferCount = StringToInt64(tokenizer.token());
        }
        state = KEY_NAME;
        break;
    }
  }
  return true;
}

}  // namespace base
<|endoftext|>


<commit_before>/*
 *
 *    Copyright (c) 2021 Project CHIP Authors
 *    All rights reserved.
 *
 *    Licensed 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 <app/clusters/ota-requestor/OTADownloader.h>
#include <app/clusters/ota-requestor/OTARequestorInterface.h>

#include "OTAImageProcessorImpl.h"

#include <sys/stat.h>

namespace chip {

CHIP_ERROR OTAImageProcessorImpl::PrepareDownload()
{
    if (mImageFile == nullptr)
    {
        ChipLogError(SoftwareUpdate, "Invalid output image file supplied");
        return CHIP_ERROR_INTERNAL;
    }

    DeviceLayer::PlatformMgr().ScheduleWork(HandlePrepareDownload, reinterpret_cast<intptr_t>(this));
    return CHIP_NO_ERROR;
}

CHIP_ERROR OTAImageProcessorImpl::Finalize()
{
    DeviceLayer::PlatformMgr().ScheduleWork(HandleFinalize, reinterpret_cast<intptr_t>(this));
    return CHIP_NO_ERROR;
}

CHIP_ERROR OTAImageProcessorImpl::Apply()
{
    DeviceLayer::PlatformMgr().ScheduleWork(HandleApply, reinterpret_cast<intptr_t>(this));
    return CHIP_NO_ERROR;
}

CHIP_ERROR OTAImageProcessorImpl::Abort()
{
    if (mImageFile == nullptr)
    {
        ChipLogError(SoftwareUpdate, "Invalid output image file supplied");
        return CHIP_ERROR_INTERNAL;
    }

    DeviceLayer::PlatformMgr().ScheduleWork(HandleAbort, reinterpret_cast<intptr_t>(this));
    return CHIP_NO_ERROR;
}

CHIP_ERROR OTAImageProcessorImpl::ProcessBlock(ByteSpan & block)
{
    if (!mOfs.is_open() || !mOfs.good())
    {
        return CHIP_ERROR_INTERNAL;
    }

    if ((block.data() == nullptr) || block.empty())
    {
        return CHIP_ERROR_INVALID_ARGUMENT;
    }

    // Store block data for HandleProcessBlock to access
    CHIP_ERROR err = SetBlock(block);
    if (err != CHIP_NO_ERROR)
    {
        ChipLogError(SoftwareUpdate, "Cannot set block data: %" CHIP_ERROR_FORMAT, err.Format());
    }

    DeviceLayer::PlatformMgr().ScheduleWork(HandleProcessBlock, reinterpret_cast<intptr_t>(this));
    return CHIP_NO_ERROR;
}

bool OTAImageProcessorImpl::IsFirstImageRun()
{
    OTARequestorInterface * requestor = chip::GetRequestorInstance();
    if (requestor == nullptr)
    {
        return false;
    }

    return requestor->GetCurrentUpdateState() == OTARequestorInterface::OTAUpdateStateEnum::kApplying;
}

CHIP_ERROR OTAImageProcessorImpl::ConfirmCurrentImage()
{
    OTARequestorInterface * requestor = chip::GetRequestorInstance();
    if (requestor == nullptr)
    {
        return CHIP_ERROR_INTERNAL;
    }

    uint32_t currentVersion;
    uint32_t targetVersion = requestor->GetTargetVersion();
    ReturnErrorOnFailure(DeviceLayer::ConfigurationMgr().GetSoftwareVersion(currentVersion));
    if (currentVersion != targetVersion)
    {
        ChipLogError(SoftwareUpdate, "Current software version = %" PRIu32 ", expected software version = %" PRIu32, currentVersion,
                     targetVersion);
        return CHIP_ERROR_INCORRECT_STATE;
    }

    return CHIP_NO_ERROR;
}

void OTAImageProcessorImpl::HandlePrepareDownload(intptr_t context)
{
    auto * imageProcessor = reinterpret_cast<OTAImageProcessorImpl *>(context);
    if (imageProcessor == nullptr)
    {
        ChipLogError(SoftwareUpdate, "ImageProcessor context is null");
        return;
    }
    else if (imageProcessor->mDownloader == nullptr)
    {
        ChipLogError(SoftwareUpdate, "mDownloader is null");
        return;
    }

    unlink(imageProcessor->mImageFile);

    imageProcessor->mHeaderParser.Init();
    imageProcessor->mOfs.open(imageProcessor->mImageFile, std::ofstream::out | std::ofstream::ate | std::ofstream::app);
    if (!imageProcessor->mOfs.good())
    {
        imageProcessor->mDownloader->OnPreparedForDownload(CHIP_ERROR_OPEN_FAILED);
        return;
    }

    imageProcessor->mDownloader->OnPreparedForDownload(CHIP_NO_ERROR);
}

void OTAImageProcessorImpl::HandleFinalize(intptr_t context)
{
    auto * imageProcessor = reinterpret_cast<OTAImageProcessorImpl *>(context);
    if (imageProcessor == nullptr)
    {
        return;
    }

    imageProcessor->mOfs.close();
    imageProcessor->ReleaseBlock();

    ChipLogProgress(SoftwareUpdate, "OTA image downloaded to %s", imageProcessor->mImageFile);
}

void OTAImageProcessorImpl::HandleApply(intptr_t context)
{
    auto * imageProcessor = reinterpret_cast<OTAImageProcessorImpl *>(context);
    VerifyOrReturn(imageProcessor != nullptr);

    OTARequestorInterface * requestor = chip::GetRequestorInstance();
    VerifyOrReturn(requestor != nullptr);

    // Move the downloaded image to the location where the new image is to be executed from
    unlink(kImageExecPath);
    rename(imageProcessor->mImageFile, kImageExecPath);
    chmod(kImageExecPath, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);

    // Shutdown the stack and expect to boot into the new image once the event loop is stopped
    DeviceLayer::PlatformMgr().ScheduleWork([](intptr_t) { DeviceLayer::PlatformMgr().HandleServerShuttingDown(); });
    DeviceLayer::PlatformMgr().ScheduleWork([](intptr_t) { DeviceLayer::PlatformMgr().StopEventLoopTask(); });
}

void OTAImageProcessorImpl::HandleAbort(intptr_t context)
{
    auto * imageProcessor = reinterpret_cast<OTAImageProcessorImpl *>(context);
    if (imageProcessor == nullptr)
    {
        return;
    }

    imageProcessor->mOfs.close();
    unlink(imageProcessor->mImageFile);
    imageProcessor->ReleaseBlock();
}

void OTAImageProcessorImpl::HandleProcessBlock(intptr_t context)
{
    auto * imageProcessor = reinterpret_cast<OTAImageProcessorImpl *>(context);
    if (imageProcessor == nullptr)
    {
        ChipLogError(SoftwareUpdate, "ImageProcessor context is null");
        return;
    }
    else if (imageProcessor->mDownloader == nullptr)
    {
        ChipLogError(SoftwareUpdate, "mDownloader is null");
        return;
    }

    ByteSpan block   = imageProcessor->mBlock;
    CHIP_ERROR error = imageProcessor->ProcessHeader(block);
    if (error != CHIP_NO_ERROR)
    {
        ChipLogError(SoftwareUpdate, "Image does not contain a valid header");
        imageProcessor->mDownloader->EndDownload(CHIP_ERROR_INVALID_FILE_IDENTIFIER);
        return;
    }

    if (!imageProcessor->mOfs.write(reinterpret_cast<const char *>(block.data()), static_cast<std::streamsize>(block.size())))
    {
        imageProcessor->mDownloader->EndDownload(CHIP_ERROR_WRITE_FAILED);
        return;
    }

    imageProcessor->mParams.downloadedBytes += block.size();
    imageProcessor->mDownloader->FetchNextData();
}

CHIP_ERROR OTAImageProcessorImpl::ProcessHeader(ByteSpan & block)
{
    if (mHeaderParser.IsInitialized())
    {
        OTAImageHeader header;
        CHIP_ERROR error = mHeaderParser.AccumulateAndDecode(block, header);

        // Needs more data to decode the header
        ReturnErrorCodeIf(error == CHIP_ERROR_BUFFER_TOO_SMALL, CHIP_NO_ERROR);
        ReturnErrorOnFailure(error);

        mParams.totalFileBytes = header.mPayloadSize;
        mHeaderParser.Clear();
    }

    return CHIP_NO_ERROR;
}

CHIP_ERROR OTAImageProcessorImpl::SetBlock(ByteSpan & block)
{
    if (!IsSpanUsable(block))
    {
        ReleaseBlock();
        return CHIP_NO_ERROR;
    }
    if (mBlock.size() < block.size())
    {
        if (!mBlock.empty())
        {
            ReleaseBlock();
        }
        uint8_t * mBlock_ptr = static_cast<uint8_t *>(chip::Platform::MemoryAlloc(block.size()));
        if (mBlock_ptr == nullptr)
        {
            return CHIP_ERROR_NO_MEMORY;
        }
        mBlock = MutableByteSpan(mBlock_ptr, block.size());
    }
    CHIP_ERROR err = CopySpanToMutableSpan(block, mBlock);
    if (err != CHIP_NO_ERROR)
    {
        ChipLogError(SoftwareUpdate, "Cannot copy block data: %" CHIP_ERROR_FORMAT, err.Format());
        return err;
    }
    return CHIP_NO_ERROR;
}

CHIP_ERROR OTAImageProcessorImpl::ReleaseBlock()
{
    if (mBlock.data() != nullptr)
    {
        chip::Platform::MemoryFree(mBlock.data());
    }

    mBlock = MutableByteSpan();
    return CHIP_NO_ERROR;
}

} // namespace chip
<commit_msg>OTA: Fix linux BlockEOF with data length zero (#22309)<commit_after>/*
 *
 *    Copyright (c) 2021 Project CHIP Authors
 *    All rights reserved.
 *
 *    Licensed 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 <app/clusters/ota-requestor/OTADownloader.h>
#include <app/clusters/ota-requestor/OTARequestorInterface.h>

#include "OTAImageProcessorImpl.h"

#include <sys/stat.h>

namespace chip {

CHIP_ERROR OTAImageProcessorImpl::PrepareDownload()
{
    if (mImageFile == nullptr)
    {
        ChipLogError(SoftwareUpdate, "Invalid output image file supplied");
        return CHIP_ERROR_INTERNAL;
    }

    DeviceLayer::PlatformMgr().ScheduleWork(HandlePrepareDownload, reinterpret_cast<intptr_t>(this));
    return CHIP_NO_ERROR;
}

CHIP_ERROR OTAImageProcessorImpl::Finalize()
{
    DeviceLayer::PlatformMgr().ScheduleWork(HandleFinalize, reinterpret_cast<intptr_t>(this));
    return CHIP_NO_ERROR;
}

CHIP_ERROR OTAImageProcessorImpl::Apply()
{
    DeviceLayer::PlatformMgr().ScheduleWork(HandleApply, reinterpret_cast<intptr_t>(this));
    return CHIP_NO_ERROR;
}

CHIP_ERROR OTAImageProcessorImpl::Abort()
{
    if (mImageFile == nullptr)
    {
        ChipLogError(SoftwareUpdate, "Invalid output image file supplied");
        return CHIP_ERROR_INTERNAL;
    }

    DeviceLayer::PlatformMgr().ScheduleWork(HandleAbort, reinterpret_cast<intptr_t>(this));
    return CHIP_NO_ERROR;
}

CHIP_ERROR OTAImageProcessorImpl::ProcessBlock(ByteSpan & block)
{
    if (!mOfs.is_open() || !mOfs.good())
    {
        return CHIP_ERROR_INTERNAL;
    }

    // Store block data for HandleProcessBlock to access
    CHIP_ERROR err = SetBlock(block);
    if (err != CHIP_NO_ERROR)
    {
        ChipLogError(SoftwareUpdate, "Cannot set block data: %" CHIP_ERROR_FORMAT, err.Format());
    }

    DeviceLayer::PlatformMgr().ScheduleWork(HandleProcessBlock, reinterpret_cast<intptr_t>(this));
    return CHIP_NO_ERROR;
}

bool OTAImageProcessorImpl::IsFirstImageRun()
{
    OTARequestorInterface * requestor = chip::GetRequestorInstance();
    if (requestor == nullptr)
    {
        return false;
    }

    return requestor->GetCurrentUpdateState() == OTARequestorInterface::OTAUpdateStateEnum::kApplying;
}

CHIP_ERROR OTAImageProcessorImpl::ConfirmCurrentImage()
{
    OTARequestorInterface * requestor = chip::GetRequestorInstance();
    if (requestor == nullptr)
    {
        return CHIP_ERROR_INTERNAL;
    }

    uint32_t currentVersion;
    uint32_t targetVersion = requestor->GetTargetVersion();
    ReturnErrorOnFailure(DeviceLayer::ConfigurationMgr().GetSoftwareVersion(currentVersion));
    if (currentVersion != targetVersion)
    {
        ChipLogError(SoftwareUpdate, "Current software version = %" PRIu32 ", expected software version = %" PRIu32, currentVersion,
                     targetVersion);
        return CHIP_ERROR_INCORRECT_STATE;
    }

    return CHIP_NO_ERROR;
}

void OTAImageProcessorImpl::HandlePrepareDownload(intptr_t context)
{
    auto * imageProcessor = reinterpret_cast<OTAImageProcessorImpl *>(context);
    if (imageProcessor == nullptr)
    {
        ChipLogError(SoftwareUpdate, "ImageProcessor context is null");
        return;
    }
    else if (imageProcessor->mDownloader == nullptr)
    {
        ChipLogError(SoftwareUpdate, "mDownloader is null");
        return;
    }

    unlink(imageProcessor->mImageFile);

    imageProcessor->mHeaderParser.Init();
    imageProcessor->mOfs.open(imageProcessor->mImageFile, std::ofstream::out | std::ofstream::ate | std::ofstream::app);
    if (!imageProcessor->mOfs.good())
    {
        imageProcessor->mDownloader->OnPreparedForDownload(CHIP_ERROR_OPEN_FAILED);
        return;
    }

    imageProcessor->mDownloader->OnPreparedForDownload(CHIP_NO_ERROR);
}

void OTAImageProcessorImpl::HandleFinalize(intptr_t context)
{
    auto * imageProcessor = reinterpret_cast<OTAImageProcessorImpl *>(context);
    if (imageProcessor == nullptr)
    {
        return;
    }

    imageProcessor->mOfs.close();
    imageProcessor->ReleaseBlock();

    ChipLogProgress(SoftwareUpdate, "OTA image downloaded to %s", imageProcessor->mImageFile);
}

void OTAImageProcessorImpl::HandleApply(intptr_t context)
{
    auto * imageProcessor = reinterpret_cast<OTAImageProcessorImpl *>(context);
    VerifyOrReturn(imageProcessor != nullptr);

    OTARequestorInterface * requestor = chip::GetRequestorInstance();
    VerifyOrReturn(requestor != nullptr);

    // Move the downloaded image to the location where the new image is to be executed from
    unlink(kImageExecPath);
    rename(imageProcessor->mImageFile, kImageExecPath);
    chmod(kImageExecPath, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);

    // Shutdown the stack and expect to boot into the new image once the event loop is stopped
    DeviceLayer::PlatformMgr().ScheduleWork([](intptr_t) { DeviceLayer::PlatformMgr().HandleServerShuttingDown(); });
    DeviceLayer::PlatformMgr().ScheduleWork([](intptr_t) { DeviceLayer::PlatformMgr().StopEventLoopTask(); });
}

void OTAImageProcessorImpl::HandleAbort(intptr_t context)
{
    auto * imageProcessor = reinterpret_cast<OTAImageProcessorImpl *>(context);
    if (imageProcessor == nullptr)
    {
        return;
    }

    imageProcessor->mOfs.close();
    unlink(imageProcessor->mImageFile);
    imageProcessor->ReleaseBlock();
}

void OTAImageProcessorImpl::HandleProcessBlock(intptr_t context)
{
    auto * imageProcessor = reinterpret_cast<OTAImageProcessorImpl *>(context);
    if (imageProcessor == nullptr)
    {
        ChipLogError(SoftwareUpdate, "ImageProcessor context is null");
        return;
    }
    else if (imageProcessor->mDownloader == nullptr)
    {
        ChipLogError(SoftwareUpdate, "mDownloader is null");
        return;
    }

    ByteSpan block   = imageProcessor->mBlock;
    CHIP_ERROR error = imageProcessor->ProcessHeader(block);
    if (error != CHIP_NO_ERROR)
    {
        ChipLogError(SoftwareUpdate, "Image does not contain a valid header");
        imageProcessor->mDownloader->EndDownload(CHIP_ERROR_INVALID_FILE_IDENTIFIER);
        return;
    }

    if (!imageProcessor->mOfs.write(reinterpret_cast<const char *>(block.data()), static_cast<std::streamsize>(block.size())))
    {
        imageProcessor->mDownloader->EndDownload(CHIP_ERROR_WRITE_FAILED);
        return;
    }

    imageProcessor->mParams.downloadedBytes += block.size();
    imageProcessor->mDownloader->FetchNextData();
}

CHIP_ERROR OTAImageProcessorImpl::ProcessHeader(ByteSpan & block)
{
    if (mHeaderParser.IsInitialized())
    {
        OTAImageHeader header;
        CHIP_ERROR error = mHeaderParser.AccumulateAndDecode(block, header);

        // Needs more data to decode the header
        ReturnErrorCodeIf(error == CHIP_ERROR_BUFFER_TOO_SMALL, CHIP_NO_ERROR);
        ReturnErrorOnFailure(error);

        mParams.totalFileBytes = header.mPayloadSize;
        mHeaderParser.Clear();
    }

    return CHIP_NO_ERROR;
}

CHIP_ERROR OTAImageProcessorImpl::SetBlock(ByteSpan & block)
{
    if (!IsSpanUsable(block))
    {
        ReleaseBlock();
        return CHIP_NO_ERROR;
    }
    if (mBlock.size() < block.size())
    {
        if (!mBlock.empty())
        {
            ReleaseBlock();
        }
        uint8_t * mBlock_ptr = static_cast<uint8_t *>(chip::Platform::MemoryAlloc(block.size()));
        if (mBlock_ptr == nullptr)
        {
            return CHIP_ERROR_NO_MEMORY;
        }
        mBlock = MutableByteSpan(mBlock_ptr, block.size());
    }
    CHIP_ERROR err = CopySpanToMutableSpan(block, mBlock);
    if (err != CHIP_NO_ERROR)
    {
        ChipLogError(SoftwareUpdate, "Cannot copy block data: %" CHIP_ERROR_FORMAT, err.Format());
        return err;
    }
    return CHIP_NO_ERROR;
}

CHIP_ERROR OTAImageProcessorImpl::ReleaseBlock()
{
    if (mBlock.data() != nullptr)
    {
        chip::Platform::MemoryFree(mBlock.data());
    }

    mBlock = MutableByteSpan();
    return CHIP_NO_ERROR;
}

} // namespace chip
<|endoftext|>


<commit_before>/*
   For more information, please see: http://software.sci.utah.edu

   The MIT License

   Copyright (c) 2009 Scientific Computing and Imaging Institute,
   University of Utah.

   Permission is hereby granted, free of charge, to any person obtaining a
   copy of this software and associated documentation files (the "Software"),
   to deal in the Software without restriction, including without limitation
   the rights to use, copy, modify, merge, publish, distribute, sublicense,
   and/or sell copies of the Software, and to permit persons to whom the
   Software is furnished to do so, subject to the following conditions:

   The above copyright notice and this permission notice shall be included
   in all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
   DEALINGS IN THE SOFTWARE.
*/

#include <Core/Algorithms/Legacy/Fields/TransformMesh/AlignMeshBoundingBoxes.h>

#include <Core/GeometryPrimitives/Transform.h>

#include <Core/Datatypes/Legacy/Field/Field.h>
#include <Core/Datatypes/Legacy/Field/VField.h>
#include <Core/Datatypes/Legacy/Field/VMesh.h>
#include <Core/Datatypes/DenseMatrix.h>

using namespace SCIRun;
using namespace SCIRun::Core::Datatypes;
using namespace SCIRun::Core::Algorithms::Fields;
using namespace SCIRun::Core::Geometry;
using namespace SCIRun::Core::Utility;
using namespace SCIRun::Core::Algorithms;

AlgorithmParameterName AlignMeshBoundingBoxesAlgo::RotateData("rotate_data");

AlignMeshBoundingBoxesAlgo::AlignMeshBoundingBoxesAlgo()
{
  addParameter(RotateData, true);
}

bool
AlignMeshBoundingBoxesAlgo::run(FieldHandle input, FieldHandle object, FieldHandle& output, MatrixHandle& transform_matrix)
{
  ScopedAlgorithmStatusReporter asr(this, "AlignMeshBoundingBoxes");

  bool rotate_data = get(RotateData).getBool();
  // Step 0:
  // Safety test:
  // Test whether we received actually a field. A handle can point to no object.
  // Using a null handle will cause the program to crash. Hence it is a good
  // policy to check all incoming handles and to see whether they point to actual
  // objects.

  if (!input)
  {
    error("No input field");
    return (false);
  }

  // Copy the field
  output.reset(input->deep_clone());

  BBox obbox = object->vmesh()->get_bounding_box();
  BBox ibbox = input->vmesh()->get_bounding_box();

  Transform transform;

  Vector iscale = ibbox.diagonal();
  Vector oscale = obbox.diagonal();
  Vector itrans(-ibbox.min());
  Vector otrans(obbox.min());
  transform.pre_translate(itrans);
  transform.pre_scale(Vector(oscale.x()/iscale.x(),oscale.y()/iscale.y(),oscale.z()/iscale.z()));
  transform.pre_translate(otrans);

  output->vmesh()->transform(transform);

  VField* field = output->vfield();
  if (field->is_vector() || field->is_tensor())
  {
    if (rotate_data)
    {
      if (field->is_vector())
      {
        VMesh::size_type sz = field->num_values();
        for (VMesh::index_type i=0; i < sz; i++)
        {
          Vector v;
          field->get_value(v,i);
          v = transform*v;
          field->set_value(v,i);
        }
      }
      if (field->is_tensor())
      {
        VMesh::size_type sz = field->num_values();
        for (VMesh::index_type i=0; i < sz; i++)
        {
          Tensor v;
          field->get_value(v,i);
          v = transform*v*transform;
          field->set_value(v,i);
        }
      }
    }
  }

  transform_matrix.reset(new DenseMatrix(transform));

  if (!transform_matrix)
  {
    error("Could not allocate transform matrix");
    return (false);
  }

#ifdef SCIRUN4_CODE_TO_BE_ENABLED_LATER
  //! Copy properties of the property manager
	output->copy_properties(input);
#endif

  // Success:
  return (true);
}

AlgorithmOutput AlignMeshBoundingBoxesAlgo::run_generic(const AlgorithmInput& input) const
{
  //get input values

  FieldHandle output;
  MatrixHandle transform;
  if (!run(input, object, output, transform))
    THROW_ALGORITHM_PROCESSING_ERROR("False returned on legacy run call.");

  AlgorithmOutput output;
  //set output values
  return output;
}
<commit_msg>Get/Set input/output<commit_after>/*
   For more information, please see: http://software.sci.utah.edu

   The MIT License

   Copyright (c) 2009 Scientific Computing and Imaging Institute,
   University of Utah.

   Permission is hereby granted, free of charge, to any person obtaining a
   copy of this software and associated documentation files (the "Software"),
   to deal in the Software without restriction, including without limitation
   the rights to use, copy, modify, merge, publish, distribute, sublicense,
   and/or sell copies of the Software, and to permit persons to whom the
   Software is furnished to do so, subject to the following conditions:

   The above copyright notice and this permission notice shall be included
   in all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
   DEALINGS IN THE SOFTWARE.
*/

#include <Core/Algorithms/Legacy/Fields/TransformMesh/AlignMeshBoundingBoxes.h>

#include <Core/GeometryPrimitives/Transform.h>

#include <Core/Datatypes/Legacy/Field/Field.h>
#include <Core/Datatypes/Legacy/Field/VField.h>
#include <Core/Datatypes/Legacy/Field/VMesh.h>
#include <Core/Datatypes/DenseMatrix.h>

using namespace SCIRun;
using namespace SCIRun::Core::Datatypes;
using namespace SCIRun::Core::Algorithms::Fields;
using namespace SCIRun::Core::Geometry;
using namespace SCIRun::Core::Utility;
using namespace SCIRun::Core::Algorithms;

AlgorithmParameterName AlignMeshBoundingBoxesAlgo::RotateData("rotate_data");

AlignMeshBoundingBoxesAlgo::AlignMeshBoundingBoxesAlgo()
{
  addParameter(RotateData, true);
}

bool
AlignMeshBoundingBoxesAlgo::run(FieldHandle input, FieldHandle object, FieldHandle& output, MatrixHandle& transform_matrix)
{
  ScopedAlgorithmStatusReporter asr(this, "AlignMeshBoundingBoxes");

  bool rotate_data = get(RotateData).getBool();
  // Step 0:
  // Safety test:
  // Test whether we received actually a field. A handle can point to no object.
  // Using a null handle will cause the program to crash. Hence it is a good
  // policy to check all incoming handles and to see whether they point to actual
  // objects.

  if (!input)
  {
    error("No input field");
    return (false);
  }

  // Copy the field
  output.reset(input->deep_clone());

  BBox obbox = object->vmesh()->get_bounding_box();
  BBox ibbox = input->vmesh()->get_bounding_box();

  Transform transform;

  Vector iscale = ibbox.diagonal();
  Vector oscale = obbox.diagonal();
  Vector itrans(-ibbox.min());
  Vector otrans(obbox.min());
  transform.pre_translate(itrans);
  transform.pre_scale(Vector(oscale.x()/iscale.x(),oscale.y()/iscale.y(),oscale.z()/iscale.z()));
  transform.pre_translate(otrans);

  output->vmesh()->transform(transform);

  VField* field = output->vfield();
  if (field->is_vector() || field->is_tensor())
  {
    if (rotate_data)
    {
      if (field->is_vector())
      {
        VMesh::size_type sz = field->num_values();
        for (VMesh::index_type i=0; i < sz; i++)
        {
          Vector v;
          field->get_value(v,i);
          v = transform*v;
          field->set_value(v,i);
        }
      }
      if (field->is_tensor())
      {
        VMesh::size_type sz = field->num_values();
        for (VMesh::index_type i=0; i < sz; i++)
        {
          Tensor v;
          field->get_value(v,i);
          v = transform*v*transform;
          field->set_value(v,i);
        }
      }
    }
  }

  transform_matrix.reset(new DenseMatrix(transform));

  if (!transform_matrix)
  {
    error("Could not allocate transform matrix");
    return (false);
  }

#ifdef SCIRUN4_CODE_TO_BE_ENABLED_LATER
  //! Copy properties of the property manager
	output->copy_properties(input);
#endif

  // Success:
  return (true);
}

AlgorithmOutput AlignMeshBoundingBoxesAlgo::run_generic(const AlgorithmInput& input) const
{
  auto inputField = input.get<Field>(InputField);
  auto object = input.get<Field>(ObjectField);

  FieldHandle output;
  MatrixHandle transform;
  if (!run(inputField, object, output, transform))
    THROW_ALGORITHM_PROCESSING_ERROR("False returned on legacy run call.");

  AlgorithmOutput output;
  output[OutputField] = output;
  output[TransformMatrix] = transform;
  return output;
}
<|endoftext|>


<commit_before>
/*

 passiveLocationPush

 Example app using samsonClient lib
 It generates random xml documents simulating information from OSS Passive Location pilot

 AUTHOR: Andreu Urruela

 */

#include <stdio.h>      // printf
#include <stdlib.h>     // exit
#include <string.h>     // memcpy
#include <iostream>     // std::cout
#include <time.h>       // strptime, struct tm

#include "au/time.h"            // au::todatString()
#include "au/string.h"          // au::str()
#include "au/Cronometer.h"      // au::Cronometer
#include "au/CommandLine.h"     // au::CommandLine

#include "samson/client/SamsonClient.h"         // samson::SamsonClient

int main( int argc , const char *argv[] )
{
	if ( argc < 2 )
	{
	   fprintf(stderr,"Usage %s rate_in_messages_per_second\n", argv[0] );
		exit(0);
	}

	size_t rate = atoll( argv[1] );

	// Small mini-buffer to generate
	char *line = (char*) malloc( 20000 );

	// Control of time and size
	au::Cronometer cronometer;

	size_t total_size = 0;
	size_t num_messages = 0;

	size_t theoretical_seconds = 0;

	while( true )
	{
	   // Generat 5 seconds data
	   fprintf(stderr,"Generatoing %d messages\n",  (int)(5 * rate) );
	   for( int i = 0 ; i < (int)(5 * rate); i++)
	   {
			size_t user_id = rand()%10000;
			int cell = rand()%20000;

			snprintf( line, 20000 ,  "<?xml version=\"1.0\" encoding=\"UTF-8\"?><ns0:AMRReport xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'  xmlns:ns0='http://O2.arcanum.vitria.com'  xsi:schemaLocation='http://O2.arcanum.vitria.com AMR.xsd'>  <SubscriberReport>    <User>      <IMSI>%lu</IMSI>      <PTMSI>FB869371</PTMSI>  <CellID>%d</CellID>   <Paging>      <Location>        <LocationArea>12124</LocationArea>        <RoutingArea>134</RoutingArea>      </Location>    </Paging>  </SubscriberReport>  <Timestamp>2011-07-21T16:07:47</Timestamp></ns0:AMRReport>" , user_id , cell );

		total_size += strlen(line);
		num_messages++;

		// Emit line to the output
		std::cout << line << "\n";

	   }

		// Detect if we need to sleep....
		theoretical_seconds += 5;

		size_t ellapsed_seconds = cronometer.diffTimeInSeconds();

		// Sleep some time to simulate a particular rate
		if( ellapsed_seconds < theoretical_seconds )
		{
			int sleep_seconds = theoretical_seconds - ellapsed_seconds;
			std::cerr << "Sleeping " << sleep_seconds << " seconds... num messages " << au::str(num_messages) << " size " << au::str( total_size , "bytes") << " time " << au::time_string(ellapsed_seconds) << " theoretical time " << au::time_string(theoretical_seconds)<<"\n";
			sleep( sleep_seconds );
		}

	}

}

<commit_msg>Minor update in generating fake passive location data<commit_after>
/*

 passiveLocationPush

 Example app using samsonClient lib
 It generates random xml documents simulating information from OSS Passive Location pilot

 AUTHOR: Andreu Urruela

 */

#include <stdio.h>      // printf
#include <stdlib.h>     // exit
#include <string.h>     // memcpy
#include <iostream>     // std::cout
#include <time.h>       // strptime, struct tm

#include "au/time.h"            // au::todatString()
#include "au/string.h"          // au::str()
#include "au/Cronometer.h"      // au::Cronometer
#include "au/CommandLine.h"     // au::CommandLine

#include "samson/client/SamsonClient.h"         // samson::SamsonClient

int main( int argc , const char *argv[] )
{
	if ( argc < 2 )
	{
	   fprintf(stderr,"Usage %s rate_in_messages_per_second\n", argv[0] );
		exit(0);
	}

	size_t rate = atoll( argv[1] );
	size_t max_kvs = 0;
	if( argc > 2 )
	  max_kvs = atoll( argv[2] );

	// Small mini-buffer to generate
	char *line = (char*) malloc( 20000 );

	// Control of time and size
	au::Cronometer cronometer;

	size_t total_size = 0;
	size_t num_messages = 0;

	size_t theoretical_seconds = 0;

	while( true )
	{
	   // Generat 5 seconds data
	   fprintf(stderr,"Generatoing %d messages\n",  (int)(5 * rate) );
	   for( int i = 0 ; i < (int)(5 * rate); i++)
	   {
			size_t user_id = rand()%10000;
			int cell = rand()%20000;

			snprintf( line, 20000 ,  "<?xml version=\"1.0\" encoding=\"UTF-8\"?><ns0:AMRReport xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'  xmlns:ns0='http://O2.arcanum.vitria.com'  xsi:schemaLocation='http://O2.arcanum.vitria.com AMR.xsd'>  <SubscriberReport>    <User>      <IMSI>%lu</IMSI>      <PTMSI>FB869371</PTMSI>  <CellID>%d</CellID>   <Paging>      <Location>        <LocationArea>12124</LocationArea>        <RoutingArea>134</RoutingArea>      </Location>    </Paging>  </SubscriberReport>  <Timestamp>2011-07-21T16:07:47</Timestamp></ns0:AMRReport>" , user_id , cell );

		total_size += strlen(line);
		num_messages++;

		// Emit line to the output
		std::cout << line << "\n";

	   }

	   if( max_kvs > 0 )
	     if( num_messages > max_kvs )
	       {
		 fprintf(stderr,"Finish since a limit of %lu kvs was specified. Generated %lu key-vaue\n", max_kvs ,num_messages);

		 return 0;
	       }

		// Detect if we need to sleep....
		theoretical_seconds += 5;

		size_t ellapsed_seconds = cronometer.diffTimeInSeconds();

		// Sleep some time to simulate a particular rate
		if( ellapsed_seconds < theoretical_seconds )
		{
			int sleep_seconds = theoretical_seconds - ellapsed_seconds;
			std::cerr << "Sleeping " << sleep_seconds << " seconds... num messages " << au::str(num_messages) << " size " << au::str( total_size , "bytes") << " time " << au::time_string(ellapsed_seconds) << " theoretical time " << au::time_string(theoretical_seconds)<<"\n";
			sleep( sleep_seconds );
		}

	}

}

<|endoftext|>


<commit_before>//
//  niihdr.cpp
//  NiftiImage
//
//  Created by Tobias Wood on 08/07/2013.
//  Copyright (c) 2013 Tobias Wood. All rights reserved.
//

#include <memory>
#include <vector>
#include <iostream>
#include <getopt.h>

#include "Nifti/Nifti.h"

using namespace std;

const string usage = "niihdr - A utility for getting information from Nifti headers.\n\
\n\
Usage: niihdr [options] file1 [other files]\n\
By default the abbreviated header will be printed for each file.\n\
Abbreviated, full and compare modes are mutually exclusive.\n\
\n\
Options:\n\
	-a, --abbrev: Print the abbreviated header (default).\n\
	-f, --full:   Print the entire header.\n\
	-c, --comp:   Compare first file to all others and print message if the.\n\
	              physical spaces are different.\n\
	-h, --help:   Print this message and quit.\n\
";

enum class Mode { Abbrev, Full, Cmp };

static Mode mode = Mode::Abbrev;

static struct option long_options[] = {
	{"abbrev", no_argument, 0, 'a'},
	{"full",   no_argument, 0, 'f'},
	{"comp",   no_argument, 0, 'c'},
	{"help",   no_argument, 0, 'h'},
	{0, 0, 0, 0}
};

int main(int argc, char **argv) {
	int indexptr = 0, c;
	while ((c = getopt_long(argc, argv, "afch", long_options, &indexptr)) != -1) {
		switch (c) {
			case 'a': mode = Mode::Abbrev; break;
			case 'f': mode = Mode::Full; break;
			case 'c': mode = Mode::Cmp; break;
			case '?': // getopt will print an error message
			case 'h':
				cout << usage << endl;
				return EXIT_FAILURE;
		}
	}
	if ((argc - optind) <= 0 ) {
		cerr << "No input image file specified." << endl;
		cout << usage << endl;
		return EXIT_FAILURE;
	}
	vector<Nifti::File> images;
	try {
		images.reserve(argc - optind); // emplace_back can still trigger copies if the vector has to be resized
		for (;optind < argc; optind++) {
			images.emplace_back(argv[optind]);
		}
	} catch (exception &e) {
		cerr << e.what() << endl;
	}

	size_t did_not_match = 0;
	if (mode == Mode::Cmp) { // Compare first image to all others and check headers are compatible
		cout << "Checking headers against: " << images[0].headerPath() << endl;
		for (auto im = images.begin() + 1; im != images.end(); im++) {
			if (!images[0].header().matchesSpace(im->header())) {
				cout << im->headerPath() << " does not match." << endl;
				did_not_match++;
			}
		}
		cout << did_not_match << " headers did not match." << endl;
	}

	for (auto& im: images) {
		Nifti::Header hdr = im.header();
		if (mode == Mode::Abbrev) {
			cout << "Image path: " << im.imagePath() << endl;
			cout << "Datatype:   " << hdr.typeInfo().name << endl;
			cout << "Dimensions: " << hdr.dims().transpose() << " (rank " << to_string(hdr.rank()) << ")" << endl;
			cout << "Voxel size: " << hdr.voxDims().transpose() << endl;
			cout << "XForm: " << endl << hdr.transform().matrix() << endl;
			(im.extensions().size() > 0) ? cout << "Has extensions." << endl : cout << "No extensions." << endl;
		} else if (mode == Mode::Full) {
			cout << "Full Nifti Header for file: " << im.imagePath() << endl;
			cout << hdr << endl;
			cout << "Number of extensions: " << im.extensions().size() << endl;
		}
	}

	return EXIT_SUCCESS;
}

<commit_msg>Added options to niihdr to print out voxdims, TR, transform. Removed compare mode.<commit_after>//
//  niihdr.cpp
//  NiftiImage
//
//  Created by Tobias Wood on 08/07/2013.
//  Copyright (c) 2013 Tobias Wood. All rights reserved.
//

#include <memory>
#include <vector>
#include <iostream>
#include <getopt.h>

#include "Nifti/Nifti.h"

using namespace std;

const string usage = "niihdr - A utility for getting information from Nifti headers.\n\
\n\
Usage: niihdr [options] file1 [other files]\n\
By default the abbreviated header will be printed for each file.\n\
Alternatively the entire header or specific fields can be printed.\n\
\n\
Options:\n\
	-a, --abbrev : Print the abbreviated header (default).\n\
	-f, --full   : Print the entire header.\n\
	-h, --help   : Print this message and quit.\n\
	--voxdims    : Print the voxel dimensions.\n\
	--tr         : Print the TR.\n\
	--xform      : Print the highest priority transform.\n\
";

static int printAbbrev = false, printFull = false,
           printVoxdims = false, printTR = false, printXform = false;
static struct option long_options[] = {
	{"abbrev",  no_argument, 0, 'a'},
	{"full",    no_argument, 0, 'f'},
	{"help",    no_argument, 0, 'h'},
	{"voxdims", no_argument, &printVoxdims, 1},
	{"tr",      no_argument, &printTR, 1},
	{"xform",   no_argument, &printXform, 1},
	{0, 0, 0, 0}
};

int main(int argc, char **argv) {
	int indexptr = 0, c;
	while ((c = getopt_long(argc, argv, "afch", long_options, &indexptr)) != -1) {
		switch (c) {
			case 'a': printAbbrev = true; printFull = false; break;
			case 'f': printFull = true; printAbbrev = false; break;
			case '?': // getopt will print an error message
			case 'h':
				cout << usage << endl;
				return EXIT_FAILURE;
		}
	}
	if ((argc - optind) <= 0 ) {
		cerr << "No input image file specified." << endl;
		cout << usage << endl;
		return EXIT_FAILURE;
	} else if (optind == 1) { // No other arguments specified
		printAbbrev = true;
	}
	vector<Nifti::File> images;
	try {
		images.reserve(argc - optind); // emplace_back can still trigger copies if the vector has to be resized
		for (;optind < argc; optind++) {
			images.emplace_back(argv[optind]);
		}
	} catch (exception &e) {
		cerr << e.what() << endl;
	}

	for (auto& im: images) {
		Nifti::Header hdr = im.header();
		if (printAbbrev) {
			cout << "Image path: " << im.imagePath() << endl;
			cout << "Datatype:   " << hdr.typeInfo().name << endl;
			cout << "Dimensions: " << hdr.dims().transpose() << " (rank " << to_string(hdr.rank()) << ")" << endl;
			cout << "Voxel size: " << hdr.voxDims().transpose() << endl;
			cout << "XForm: " << endl << hdr.transform().matrix() << endl;
			(im.extensions().size() > 0) ? cout << "Has extensions." << endl : cout << "No extensions." << endl;
		}
		if (printFull) {
			cout << "Full Nifti Header for file: " << im.imagePath() << endl;
			cout << hdr << endl;
			cout << "Number of extensions: " << im.extensions().size() << endl;
		}
		if (printVoxdims) {
			if (hdr.rank() > 3) {
				cout << hdr.voxDims().head(3).transpose() << endl;
			} else {
				cout << hdr.voxDims() << endl;
			}
		}
		if (printTR) { cout << hdr.voxDim(4) << endl; }
		if (printXform) { cout << hdr.transform().matrix() << endl; }
	}

	return EXIT_SUCCESS;
}

<|endoftext|>


<commit_before>#include "CommonData.hpp"
#include "Config.hpp"
#include "EventInputQueue.hpp"
#include "FlagStatus.hpp"
#include "GlobalLock.hpp"
#include "ListHookedConsumer.hpp"

namespace org_pqrs_KeyRemap4MacBook {
  namespace {
    ListHookedConsumer listHookedConsumer;
  }

  ListHookedConsumer&
  ListHookedConsumer::instance(void)
  {
    return listHookedConsumer;
  }

  ListHookedConsumer::Item::Item(IOHIDevice* p) : ListHookedDevice::Item(p),
    orig_keyboardSpecialEventAction_(NULL),
    orig_keyboardSpecialEventTarget_(NULL)
  {}

  ListHookedConsumer::Item::~Item(void)
  {
    IOLOG_DEBUG("ListHookedConsumer::Item::~Item()\n");
    restoreEventAction();
  }

  // ----------------------------------------------------------------------
  bool
  ListHookedConsumer::Item::refresh(void)
  {
    if (! device_) goto restore;

    {
      const char* name = device_->getName();
      if (! name) goto restore;

      if (! ListHookedDevice::Item::isConsumer(name)) goto restore;
    }

    // ------------------------------------------------------------
    if (! Config::get_initialized()) {
      goto restore;
    }
    if (Config::get_essential_config(BRIDGE_ESSENTIAL_CONFIG_INDEX_general_dont_remap_apple_keyboard) &&
        isEqualVendor(DeviceVendor::APPLE_COMPUTER)) {
      goto restore;
    }
    // Logitech USB Headset
    if (isEqualVendorProduct(DeviceVendor::LOGITECH, DeviceProduct::LOGITECH_USB_HEADSET)) {
      goto restore;
    }
    // Logitech Cordless Presenter
    if (Config::get_essential_config(BRIDGE_ESSENTIAL_CONFIG_INDEX_general_dont_remap_logitech_cordless_presenter) &&
        isEqualVendorProduct(DeviceVendor::LOGITECH, DeviceProduct::LOGITECH_CORDLESS_PRESENTER)) {
      goto restore;
    }
#if 0
    // Apple Internal Keyboard
    if (isEqualVendorProduct(DeviceVendor::APPLE_COMPUTER, DeviceProduct::APPLE_INTERNAL_KEYBOARD_TRACKPAD_0x021a)) {
      goto restore;
    }
#endif
#if 0
    // Apple External Keyboard
    if (isEqualVendorProduct(DeviceVendor::APPLE_COMPUTER, DeviceProduct::APPLE_ALUMINUM_KEYBOARD_JIS)) {
      goto restore;
    }
#endif

    return replaceEventAction();

  restore:
    return restoreEventAction();
  }

  bool
  ListHookedConsumer::Item::replaceEventAction(void)
  {
    if (! device_) return false;

    IOHIKeyboard* kbd = OSDynamicCast(IOHIKeyboard, device_);
    if (! kbd) return false;

    KeyboardSpecialEventCallback callback = reinterpret_cast<KeyboardSpecialEventCallback>(kbd->_keyboardSpecialEventAction);
    if (callback == EventInputQueue::push_KeyboardSpecialEventCallback) return false;

    // ------------------------------------------------------------
    IOLOG_DEBUG("HookedConsumer::replaceEventAction device_:%p\n", device_);

    orig_keyboardSpecialEventAction_ = callback;
    orig_keyboardSpecialEventTarget_ = kbd->_keyboardSpecialEventTarget;

    kbd->_keyboardSpecialEventAction = reinterpret_cast<KeyboardSpecialEventAction>(EventInputQueue::push_KeyboardSpecialEventCallback);

    return true;
  }

  bool
  ListHookedConsumer::Item::restoreEventAction(void)
  {
    if (! device_) return false;

    IOHIKeyboard* kbd = OSDynamicCast(IOHIKeyboard, device_);
    if (! kbd) return false;

    KeyboardSpecialEventCallback callback = reinterpret_cast<KeyboardSpecialEventCallback>(kbd->_keyboardSpecialEventAction);
    if (callback != EventInputQueue::push_KeyboardSpecialEventCallback) return false;

    // ----------------------------------------
    IOLOG_DEBUG("HookedConsumer::restoreEventAction device_:%p\n", device_);

    kbd->_keyboardSpecialEventAction = reinterpret_cast<KeyboardSpecialEventAction>(orig_keyboardSpecialEventAction_);

    orig_keyboardSpecialEventAction_ = NULL;
    orig_keyboardSpecialEventTarget_ = NULL;

    return true;
  }

  // ======================================================================
  void
  ListHookedConsumer::Item::apply(const Params_KeyboardSpecialEventCallback& params)
  {
    if (params.key >= ConsumerKeyCode::VK__BEGIN__) {
      // Invalid keycode
      IOLOG_ERROR("%s invalid key:%d\n", __PRETTY_FUNCTION__, params.key.get());
      return;
    }
    if (params.flags.isVirtualModifiersOn()) {
      IOLOG_ERROR("%s invalid flags:%d\n", __PRETTY_FUNCTION__, params.flags.get());
      return;
    }

    // ------------------------------------------------------------
    KeyboardSpecialEventCallback callback = orig_keyboardSpecialEventAction_;
    if (! callback) return;

    OSObject* target = orig_keyboardSpecialEventTarget_;
    if (! target) return;

    OSObject* sender = OSDynamicCast(OSObject, device_);
    if (! sender) return;

    const AbsoluteTime& ts = CommonData::getcurrent_ts();
    OSObject* refcon = NULL;

    Params_KeyboardSpecialEventCallback::log(false, params.eventType, params.flags, params.key,
                                             params.flavor, params.guid, params.repeat);
    {
      // We need to unlock the global lock while we are calling the callback function.
      // For more information, See ListHookedKeyboard::Item::apply(const Params_KeyboardEventCallBack& params)
      GlobalLock::ScopedUnlock lk;
      callback(target, params.eventType.get(), params.flags.get(), params.key.get(),
               params.flavor, params.guid, params.repeat, ts, sender, refcon);
    }
  }

  void
  ListHookedConsumer::Item::disableNumLock(void)
  {
    IOHIKeyboard* kbd = OSDynamicCast(IOHIKeyboard, device_);
    if (! kbd) return;

    if (kbd->numLock()) {
      kbd->setNumLock(false);
    }
  }

  void
  ListHookedConsumer::apply(const Params_KeyboardSpecialEventCallback& params)
  {
    ListHookedConsumer::Item* p = static_cast<ListHookedConsumer::Item*>(get_replaced());
    if (p) {
      p->apply(params);
    }
  }

  void
  ListHookedConsumer::disableNumLock(void)
  {
    ListHookedConsumer::Item* p = static_cast<ListHookedConsumer::Item*>(get_replaced());
    if (p) {
      p->disableNumLock();
    }
  }
}
<commit_msg>use GlobalLock::ScopedUnlock before call setNumLock<commit_after>#include "CommonData.hpp"
#include "Config.hpp"
#include "EventInputQueue.hpp"
#include "FlagStatus.hpp"
#include "GlobalLock.hpp"
#include "ListHookedConsumer.hpp"

namespace org_pqrs_KeyRemap4MacBook {
  namespace {
    ListHookedConsumer listHookedConsumer;
  }

  ListHookedConsumer&
  ListHookedConsumer::instance(void)
  {
    return listHookedConsumer;
  }

  ListHookedConsumer::Item::Item(IOHIDevice* p) : ListHookedDevice::Item(p),
    orig_keyboardSpecialEventAction_(NULL),
    orig_keyboardSpecialEventTarget_(NULL)
  {}

  ListHookedConsumer::Item::~Item(void)
  {
    IOLOG_DEBUG("ListHookedConsumer::Item::~Item()\n");
    restoreEventAction();
  }

  // ----------------------------------------------------------------------
  bool
  ListHookedConsumer::Item::refresh(void)
  {
    if (! device_) goto restore;

    {
      const char* name = device_->getName();
      if (! name) goto restore;

      if (! ListHookedDevice::Item::isConsumer(name)) goto restore;
    }

    // ------------------------------------------------------------
    if (! Config::get_initialized()) {
      goto restore;
    }
    if (Config::get_essential_config(BRIDGE_ESSENTIAL_CONFIG_INDEX_general_dont_remap_apple_keyboard) &&
        isEqualVendor(DeviceVendor::APPLE_COMPUTER)) {
      goto restore;
    }
    // Logitech USB Headset
    if (isEqualVendorProduct(DeviceVendor::LOGITECH, DeviceProduct::LOGITECH_USB_HEADSET)) {
      goto restore;
    }
    // Logitech Cordless Presenter
    if (Config::get_essential_config(BRIDGE_ESSENTIAL_CONFIG_INDEX_general_dont_remap_logitech_cordless_presenter) &&
        isEqualVendorProduct(DeviceVendor::LOGITECH, DeviceProduct::LOGITECH_CORDLESS_PRESENTER)) {
      goto restore;
    }
#if 0
    // Apple Internal Keyboard
    if (isEqualVendorProduct(DeviceVendor::APPLE_COMPUTER, DeviceProduct::APPLE_INTERNAL_KEYBOARD_TRACKPAD_0x021a)) {
      goto restore;
    }
#endif
#if 0
    // Apple External Keyboard
    if (isEqualVendorProduct(DeviceVendor::APPLE_COMPUTER, DeviceProduct::APPLE_ALUMINUM_KEYBOARD_JIS)) {
      goto restore;
    }
#endif

    return replaceEventAction();

  restore:
    return restoreEventAction();
  }

  bool
  ListHookedConsumer::Item::replaceEventAction(void)
  {
    if (! device_) return false;

    IOHIKeyboard* kbd = OSDynamicCast(IOHIKeyboard, device_);
    if (! kbd) return false;

    KeyboardSpecialEventCallback callback = reinterpret_cast<KeyboardSpecialEventCallback>(kbd->_keyboardSpecialEventAction);
    if (callback == EventInputQueue::push_KeyboardSpecialEventCallback) return false;

    // ------------------------------------------------------------
    IOLOG_DEBUG("HookedConsumer::replaceEventAction device_:%p\n", device_);

    orig_keyboardSpecialEventAction_ = callback;
    orig_keyboardSpecialEventTarget_ = kbd->_keyboardSpecialEventTarget;

    kbd->_keyboardSpecialEventAction = reinterpret_cast<KeyboardSpecialEventAction>(EventInputQueue::push_KeyboardSpecialEventCallback);

    return true;
  }

  bool
  ListHookedConsumer::Item::restoreEventAction(void)
  {
    if (! device_) return false;

    IOHIKeyboard* kbd = OSDynamicCast(IOHIKeyboard, device_);
    if (! kbd) return false;

    KeyboardSpecialEventCallback callback = reinterpret_cast<KeyboardSpecialEventCallback>(kbd->_keyboardSpecialEventAction);
    if (callback != EventInputQueue::push_KeyboardSpecialEventCallback) return false;

    // ----------------------------------------
    IOLOG_DEBUG("HookedConsumer::restoreEventAction device_:%p\n", device_);

    kbd->_keyboardSpecialEventAction = reinterpret_cast<KeyboardSpecialEventAction>(orig_keyboardSpecialEventAction_);

    orig_keyboardSpecialEventAction_ = NULL;
    orig_keyboardSpecialEventTarget_ = NULL;

    return true;
  }

  // ======================================================================
  void
  ListHookedConsumer::Item::apply(const Params_KeyboardSpecialEventCallback& params)
  {
    if (params.key >= ConsumerKeyCode::VK__BEGIN__) {
      // Invalid keycode
      IOLOG_ERROR("%s invalid key:%d\n", __PRETTY_FUNCTION__, params.key.get());
      return;
    }
    if (params.flags.isVirtualModifiersOn()) {
      IOLOG_ERROR("%s invalid flags:%d\n", __PRETTY_FUNCTION__, params.flags.get());
      return;
    }

    // ------------------------------------------------------------
    KeyboardSpecialEventCallback callback = orig_keyboardSpecialEventAction_;
    if (! callback) return;

    OSObject* target = orig_keyboardSpecialEventTarget_;
    if (! target) return;

    OSObject* sender = OSDynamicCast(OSObject, device_);
    if (! sender) return;

    const AbsoluteTime& ts = CommonData::getcurrent_ts();
    OSObject* refcon = NULL;

    Params_KeyboardSpecialEventCallback::log(false, params.eventType, params.flags, params.key,
                                             params.flavor, params.guid, params.repeat);
    {
      // We need to unlock the global lock while we are calling the callback function.
      // For more information, See ListHookedKeyboard::Item::apply(const Params_KeyboardEventCallBack& params)
      GlobalLock::ScopedUnlock lk;
      callback(target, params.eventType.get(), params.flags.get(), params.key.get(),
               params.flavor, params.guid, params.repeat, ts, sender, refcon);
    }
  }

  void
  ListHookedConsumer::Item::disableNumLock(void)
  {
    IOHIKeyboard* kbd = OSDynamicCast(IOHIKeyboard, device_);
    if (! kbd) return;

    GlobalLock::ScopedUnlock lk;
    if (kbd->numLock()) {
      kbd->setNumLock(false);
    }
  }

  void
  ListHookedConsumer::apply(const Params_KeyboardSpecialEventCallback& params)
  {
    ListHookedConsumer::Item* p = static_cast<ListHookedConsumer::Item*>(get_replaced());
    if (p) {
      p->apply(params);
    }
  }

  void
  ListHookedConsumer::disableNumLock(void)
  {
    ListHookedConsumer::Item* p = static_cast<ListHookedConsumer::Item*>(get_replaced());
    if (p) {
      p->disableNumLock();
    }
  }
}
<|endoftext|>


<commit_before>// (C) Copyright 2017, Google Inc.
// Licensed 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>
#include <utility>

#include "absl/strings/str_format.h"	// for absl::StrFormat

#include "include_gunit.h"

#include <tesseract/serialis.h>
#include "shapetable.h"
#include "unicharset.h"

namespace tesseract {

#ifndef DISABLED_LEGACY_ENGINE

static std::string TmpNameToPath(const std::string& name) {
  return file::JoinPath(FLAGS_test_tmpdir, name);
}

// Sets up a simple shape with some unichars.
static void Setup352(int font_id, Shape* shape) {
  shape->AddToShape(3, font_id);
  shape->AddToShape(5, font_id);
  shape->AddToShape(2, font_id);
}

// Verifies some properties of the 352 shape.
static void Expect352(int font_id, const Shape& shape) {
  EXPECT_EQ(3, shape.size());
  EXPECT_TRUE(shape.ContainsUnichar(2));
  EXPECT_TRUE(shape.ContainsUnichar(3));
  EXPECT_TRUE(shape.ContainsUnichar(5));
  EXPECT_FALSE(shape.ContainsUnichar(1));
  EXPECT_TRUE(shape.ContainsUnicharAndFont(2, font_id));
  EXPECT_FALSE(shape.ContainsUnicharAndFont(2, font_id - 1));
  EXPECT_FALSE(shape.ContainsUnicharAndFont(font_id, 2));
  // It should be a subset of itself.
  EXPECT_TRUE(shape.IsSubsetOf(shape));
}

#endif

// The fixture for testing Shape.
class ShapeTest : public testing::Test {
 protected:
  void SetUp() {
    std::locale::global(std::locale(""));
  }
};

// Tests that a Shape works as expected for all the basic functions.
TEST_F(ShapeTest, BasicTest) {
#ifdef DISABLED_LEGACY_ENGINE
  // Skip test because Shape is missing.
  GTEST_SKIP();
#else
  Shape shape1;
  EXPECT_EQ(0, shape1.size());
  Setup352(101, &shape1);
  Expect352(101, shape1);
  // It should still work after file I/O.
  std::string filename = TmpNameToPath("shapefile");
  FILE* fp = fopen(filename.c_str(), "wb");
  EXPECT_TRUE(fp != nullptr);
  EXPECT_TRUE(shape1.Serialize(fp));
  fclose(fp);
  TFile tfp;
  EXPECT_TRUE(tfp.Open(filename.c_str(), nullptr));
  Shape shape2;
  EXPECT_TRUE(shape2.DeSerialize(&tfp));
  Expect352(101, shape2);
  // They should be subsets of each other.
  EXPECT_TRUE(shape1.IsSubsetOf(shape2));
  EXPECT_TRUE(shape2.IsSubsetOf(shape1));
  // They should be equal unichars.
  EXPECT_TRUE(shape1.IsEqualUnichars(&shape2));
  // and still pass afterwards.
  Expect352(101, shape1);
  Expect352(101, shape2);
#endif
}

// Tests AddShape separately, as it takes quite a bit of work.
TEST_F(ShapeTest, AddShapeTest) {
#ifdef DISABLED_LEGACY_ENGINE
  // Skip test because Shape is missing.
  GTEST_SKIP();
#else
  Shape shape1;
  Setup352(101, &shape1);
  Expect352(101, shape1);
  // Now setup a different shape with different content.
  Shape shape2;
  shape2.AddToShape(3, 101);  // Duplicates shape1.
  shape2.AddToShape(5, 110);  // Different font to shape1.
  shape2.AddToShape(7, 101);  // Different unichar to shape1.
  // They should NOT be subsets of each other.
  EXPECT_FALSE(shape1.IsSubsetOf(shape2));
  EXPECT_FALSE(shape2.IsSubsetOf(shape1));
  // Now add shape2 to shape1.
  shape1.AddShape(shape2);
  // Test subsets again.
  EXPECT_FALSE(shape1.IsSubsetOf(shape2));
  EXPECT_TRUE(shape2.IsSubsetOf(shape1));
  EXPECT_EQ(4, shape1.size());
  EXPECT_FALSE(shape1.ContainsUnichar(1));
  EXPECT_TRUE(shape1.ContainsUnicharAndFont(5, 101));
  EXPECT_TRUE(shape1.ContainsUnicharAndFont(5, 110));
  EXPECT_FALSE(shape1.ContainsUnicharAndFont(3, 110));
  EXPECT_FALSE(shape1.ContainsUnicharAndFont(7, 110));
  EXPECT_FALSE(shape1.IsEqualUnichars(&shape2));
#endif
}

// The fixture for testing Shape.
class ShapeTableTest : public testing::Test {};

// Tests that a Shape works as expected for all the basic functions.
TEST_F(ShapeTableTest, FullTest) {
#ifdef DISABLED_LEGACY_ENGINE
  // Skip test because Shape is missing.
  GTEST_SKIP();
#else
  Shape shape1;
  Setup352(101, &shape1);
  // Build a shape table with the same data, but in separate shapes.
  UNICHARSET unicharset;
  unicharset.unichar_insert(" ");
  for (int i = 1; i <= 10; ++i) {
    std::string class_str = absl::StrFormat("class%d", i);
    unicharset.unichar_insert(class_str.c_str());
  }
  ShapeTable st(unicharset);
  EXPECT_EQ(0, st.AddShape(3, 101));
  EXPECT_EQ(1, st.AddShape(5, 101));
  EXPECT_EQ(2, st.AddShape(2, 101));
  EXPECT_EQ(3, st.NumShapes());
  Expect352(101, shape1);
  EXPECT_EQ(3, st.AddShape(shape1));
  for (int i = 0; i < 3; ++i) {
    EXPECT_FALSE(st.MutableShape(i)->IsEqualUnichars(&shape1));
  }
  EXPECT_TRUE(st.MutableShape(3)->IsEqualUnichars(&shape1));
  EXPECT_TRUE(st.AnyMultipleUnichars());
  st.DeleteShape(3);
  EXPECT_FALSE(st.AnyMultipleUnichars());

  // Now merge to make a single shape like shape1.
  EXPECT_EQ(1, st.MasterUnicharCount(0));
  st.MergeShapes(0, 1);
  EXPECT_EQ(3, st.MergedUnicharCount(1, 2));
  st.MergeShapes(1, 2);
  for (int i = 0; i < 3; ++i) {
    EXPECT_EQ(3, st.MasterUnicharCount(i));
    // Master font count is the sum of all the font counts in the shape, not
    // the actual number of different fonts in the shape.
    EXPECT_EQ(3, st.MasterFontCount(i));
  }
  EXPECT_EQ(0, st.MasterDestinationIndex(1));
  EXPECT_EQ(0, st.MasterDestinationIndex(2));
  ShapeTable st2;
  st2.AppendMasterShapes(st, nullptr);
  EXPECT_EQ(1, st.NumMasterShapes());
  EXPECT_EQ(1, st2.NumShapes());
  EXPECT_TRUE(st2.MutableShape(0)->IsEqualUnichars(&shape1));
  EXPECT_TRUE(st2.AnyMultipleUnichars());
#endif
}

}  // namespace
<commit_msg>[test] Correctly use assert instead of expect.<commit_after>// (C) Copyright 2017, Google Inc.
// Licensed 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>
#include <utility>

#include "absl/strings/str_format.h"	// for absl::StrFormat

#include "include_gunit.h"

#include <tesseract/serialis.h>
#include "shapetable.h"
#include "unicharset.h"

namespace tesseract {

#ifndef DISABLED_LEGACY_ENGINE

static std::string TmpNameToPath(const std::string& name) {
  return file::JoinPath(FLAGS_test_tmpdir, name);
}

// Sets up a simple shape with some unichars.
static void Setup352(int font_id, Shape* shape) {
  shape->AddToShape(3, font_id);
  shape->AddToShape(5, font_id);
  shape->AddToShape(2, font_id);
}

// Verifies some properties of the 352 shape.
static void Expect352(int font_id, const Shape& shape) {
  EXPECT_EQ(3, shape.size());
  EXPECT_TRUE(shape.ContainsUnichar(2));
  EXPECT_TRUE(shape.ContainsUnichar(3));
  EXPECT_TRUE(shape.ContainsUnichar(5));
  EXPECT_FALSE(shape.ContainsUnichar(1));
  EXPECT_TRUE(shape.ContainsUnicharAndFont(2, font_id));
  EXPECT_FALSE(shape.ContainsUnicharAndFont(2, font_id - 1));
  EXPECT_FALSE(shape.ContainsUnicharAndFont(font_id, 2));
  // It should be a subset of itself.
  EXPECT_TRUE(shape.IsSubsetOf(shape));
}

#endif

// The fixture for testing Shape.
class ShapeTest : public testing::Test {
 protected:
  void SetUp() {
    std::locale::global(std::locale(""));
  }
};

// Tests that a Shape works as expected for all the basic functions.
TEST_F(ShapeTest, BasicTest) {
#ifdef DISABLED_LEGACY_ENGINE
  // Skip test because Shape is missing.
  GTEST_SKIP();
#else
  Shape shape1;
  EXPECT_EQ(0, shape1.size());
  Setup352(101, &shape1);
  Expect352(101, shape1);
  // It should still work after file I/O.
  std::string filename = TmpNameToPath("shapefile");
  FILE* fp = fopen(filename.c_str(), "wb");
  ASSERT_TRUE(fp != nullptr);
  EXPECT_TRUE(shape1.Serialize(fp));
  fclose(fp);
  TFile tfp;
  EXPECT_TRUE(tfp.Open(filename.c_str(), nullptr));
  Shape shape2;
  EXPECT_TRUE(shape2.DeSerialize(&tfp));
  Expect352(101, shape2);
  // They should be subsets of each other.
  EXPECT_TRUE(shape1.IsSubsetOf(shape2));
  EXPECT_TRUE(shape2.IsSubsetOf(shape1));
  // They should be equal unichars.
  EXPECT_TRUE(shape1.IsEqualUnichars(&shape2));
  // and still pass afterwards.
  Expect352(101, shape1);
  Expect352(101, shape2);
#endif
}

// Tests AddShape separately, as it takes quite a bit of work.
TEST_F(ShapeTest, AddShapeTest) {
#ifdef DISABLED_LEGACY_ENGINE
  // Skip test because Shape is missing.
  GTEST_SKIP();
#else
  Shape shape1;
  Setup352(101, &shape1);
  Expect352(101, shape1);
  // Now setup a different shape with different content.
  Shape shape2;
  shape2.AddToShape(3, 101);  // Duplicates shape1.
  shape2.AddToShape(5, 110);  // Different font to shape1.
  shape2.AddToShape(7, 101);  // Different unichar to shape1.
  // They should NOT be subsets of each other.
  EXPECT_FALSE(shape1.IsSubsetOf(shape2));
  EXPECT_FALSE(shape2.IsSubsetOf(shape1));
  // Now add shape2 to shape1.
  shape1.AddShape(shape2);
  // Test subsets again.
  EXPECT_FALSE(shape1.IsSubsetOf(shape2));
  EXPECT_TRUE(shape2.IsSubsetOf(shape1));
  EXPECT_EQ(4, shape1.size());
  EXPECT_FALSE(shape1.ContainsUnichar(1));
  EXPECT_TRUE(shape1.ContainsUnicharAndFont(5, 101));
  EXPECT_TRUE(shape1.ContainsUnicharAndFont(5, 110));
  EXPECT_FALSE(shape1.ContainsUnicharAndFont(3, 110));
  EXPECT_FALSE(shape1.ContainsUnicharAndFont(7, 110));
  EXPECT_FALSE(shape1.IsEqualUnichars(&shape2));
#endif
}

// The fixture for testing Shape.
class ShapeTableTest : public testing::Test {};

// Tests that a Shape works as expected for all the basic functions.
TEST_F(ShapeTableTest, FullTest) {
#ifdef DISABLED_LEGACY_ENGINE
  // Skip test because Shape is missing.
  GTEST_SKIP();
#else
  Shape shape1;
  Setup352(101, &shape1);
  // Build a shape table with the same data, but in separate shapes.
  UNICHARSET unicharset;
  unicharset.unichar_insert(" ");
  for (int i = 1; i <= 10; ++i) {
    std::string class_str = absl::StrFormat("class%d", i);
    unicharset.unichar_insert(class_str.c_str());
  }
  ShapeTable st(unicharset);
  EXPECT_EQ(0, st.AddShape(3, 101));
  EXPECT_EQ(1, st.AddShape(5, 101));
  EXPECT_EQ(2, st.AddShape(2, 101));
  EXPECT_EQ(3, st.NumShapes());
  Expect352(101, shape1);
  EXPECT_EQ(3, st.AddShape(shape1));
  for (int i = 0; i < 3; ++i) {
    EXPECT_FALSE(st.MutableShape(i)->IsEqualUnichars(&shape1));
  }
  EXPECT_TRUE(st.MutableShape(3)->IsEqualUnichars(&shape1));
  EXPECT_TRUE(st.AnyMultipleUnichars());
  st.DeleteShape(3);
  EXPECT_FALSE(st.AnyMultipleUnichars());

  // Now merge to make a single shape like shape1.
  EXPECT_EQ(1, st.MasterUnicharCount(0));
  st.MergeShapes(0, 1);
  EXPECT_EQ(3, st.MergedUnicharCount(1, 2));
  st.MergeShapes(1, 2);
  for (int i = 0; i < 3; ++i) {
    EXPECT_EQ(3, st.MasterUnicharCount(i));
    // Master font count is the sum of all the font counts in the shape, not
    // the actual number of different fonts in the shape.
    EXPECT_EQ(3, st.MasterFontCount(i));
  }
  EXPECT_EQ(0, st.MasterDestinationIndex(1));
  EXPECT_EQ(0, st.MasterDestinationIndex(2));
  ShapeTable st2;
  st2.AppendMasterShapes(st, nullptr);
  EXPECT_EQ(1, st.NumMasterShapes());
  EXPECT_EQ(1, st2.NumShapes());
  EXPECT_TRUE(st2.MutableShape(0)->IsEqualUnichars(&shape1));
  EXPECT_TRUE(st2.AnyMultipleUnichars());
#endif
}

}  // namespace
<|endoftext|>


<commit_before>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/gtk/options/content_settings_window_gtk.h"

#include <string>

#include "app/l10n_util.h"
#include "base/command_line.h"
#include "base/message_loop.h"
#include "chrome/browser/browser.h"
#include "chrome/browser/browser_list.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/gtk/accessible_widget_helper_gtk.h"
#include "chrome/browser/gtk/browser_window_gtk.h"
#include "chrome/browser/gtk/gtk_util.h"
#include "chrome/browser/pref_service.h"
#include "chrome/browser/profile.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/content_settings_types.h"
#include "chrome/common/notification_service.h"
#include "chrome/common/pref_names.h"
#include "grit/chromium_strings.h"
#include "grit/generated_resources.h"
#include "grit/locale_settings.h"

namespace {

// The singleton options window object.
ContentSettingsWindowGtk* settings_window = NULL;

}  // namespace

// static
void ContentSettingsWindowGtk::Show(GtkWindow* parent,
                                    ContentSettingsType page,
                                    Profile* profile) {
  DCHECK(profile);

  if (!settings_window) {
    // Creating and initializing a bunch of controls generates a bunch of
    // spurious events as control values change. Temporarily suppress
    // accessibility events until the window is created.
    profile->PauseAccessibilityEvents();

    // Create the options window.
    settings_window = new ContentSettingsWindowGtk(parent, profile);

    // Resume accessibility events.
    profile->ResumeAccessibilityEvents();
  }
  settings_window->ShowContentSettingsTab(page);
}

ContentSettingsWindowGtk::ContentSettingsWindowGtk(GtkWindow* parent,
                                                   Profile* profile)
    : profile_(profile),
      cookie_page_(profile),
      image_page_(profile, CONTENT_SETTINGS_TYPE_IMAGES),
      javascript_page_(profile, CONTENT_SETTINGS_TYPE_JAVASCRIPT),
      plugin_page_(profile, CONTENT_SETTINGS_TYPE_PLUGINS),
      popup_page_(profile, CONTENT_SETTINGS_TYPE_POPUPS),
      geolocation_page_(profile, CONTENT_SETTINGS_TYPE_GEOLOCATION) {
  // We don't need to observe changes in this value.
  last_selected_page_.Init(prefs::kContentSettingsWindowLastTabIndex,
                           profile->GetPrefs(), NULL);

  std::string dialog_name = l10n_util::GetStringUTF8(
      IDS_CONTENT_SETTINGS_TITLE);
  dialog_ = gtk_dialog_new_with_buttons(
      dialog_name.c_str(),
      parent,
      static_cast<GtkDialogFlags>(GTK_DIALOG_MODAL | GTK_DIALOG_NO_SEPARATOR),
      GTK_STOCK_CLOSE,
      GTK_RESPONSE_CLOSE,
      NULL);

  accessible_widget_helper_.reset(new AccessibleWidgetHelper(
      dialog_, profile_));
  accessible_widget_helper_->SendOpenWindowNotification(dialog_name);

  gtk_window_set_default_size(GTK_WINDOW(dialog_), 500, -1);
  // Allow browser windows to go in front of the options dialog in metacity.
  gtk_window_set_type_hint(GTK_WINDOW(dialog_), GDK_WINDOW_TYPE_HINT_NORMAL);
  gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(dialog_)->vbox),
                      gtk_util::kContentAreaSpacing);
  gtk_util::SetWindowIcon(GTK_WINDOW(dialog_));

  notebook_ = gtk_notebook_new();

  gtk_notebook_append_page(
      GTK_NOTEBOOK(notebook_),
      cookie_page_.get_page_widget(),
      gtk_label_new(
          l10n_util::GetStringUTF8(IDS_COOKIES_TAB_LABEL).c_str()));
  gtk_notebook_append_page(
      GTK_NOTEBOOK(notebook_),
      image_page_.get_page_widget(),
      gtk_label_new(
          l10n_util::GetStringUTF8(IDS_IMAGES_TAB_LABEL).c_str()));
  gtk_notebook_append_page(
      GTK_NOTEBOOK(notebook_),
      javascript_page_.get_page_widget(),
      gtk_label_new(
          l10n_util::GetStringUTF8(IDS_JAVASCRIPT_TAB_LABEL).c_str()));
  gtk_notebook_append_page(
      GTK_NOTEBOOK(notebook_),
      plugin_page_.get_page_widget(),
      gtk_label_new(
          l10n_util::GetStringUTF8(IDS_PLUGIN_TAB_LABEL).c_str()));
  gtk_notebook_append_page(
      GTK_NOTEBOOK(notebook_),
      popup_page_.get_page_widget(),
      gtk_label_new(
          l10n_util::GetStringUTF8(IDS_POPUP_TAB_LABEL).c_str()));
  gtk_notebook_append_page(
      GTK_NOTEBOOK(notebook_),
      geolocation_page_.get_page_widget(),
      gtk_label_new(
          l10n_util::GetStringUTF8(IDS_GEOLOCATION_TAB_LABEL).c_str()));

  gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog_)->vbox), notebook_);

  DCHECK_EQ(gtk_notebook_get_n_pages(GTK_NOTEBOOK(notebook_)),
            CONTENT_SETTINGS_NUM_TYPES);

  // Need to show the notebook before connecting switch-page signal, otherwise
  // we'll immediately get a signal switching to page 0 and overwrite our
  // last_selected_page_ value.
  gtk_widget_show_all(dialog_);

  g_signal_connect(notebook_, "switch-page",
                   G_CALLBACK(OnSwitchPageThunk), this);

  // We only have one button and don't do any special handling, so just hook it
  // directly to gtk_widget_destroy.
  g_signal_connect(dialog_, "response", G_CALLBACK(gtk_widget_destroy), NULL);

  g_signal_connect(dialog_, "destroy", G_CALLBACK(OnWindowDestroyThunk), this);
}

ContentSettingsWindowGtk::~ContentSettingsWindowGtk() {
}

void ContentSettingsWindowGtk::ShowContentSettingsTab(
    ContentSettingsType page) {
  if (Browser* b = BrowserList::GetLastActive()) {
    gtk_util::CenterOverWindow(GTK_WINDOW(dialog_),
                               b->window()->GetNativeHandle());
  }

  // Bring options window to front if it already existed and isn't already
  // in front
  gtk_window_present_with_time(GTK_WINDOW(dialog_),
                               gtk_get_current_event_time());

  if (page == CONTENT_SETTINGS_TYPE_DEFAULT) {
    // Remember the last visited page from local state.
    page = static_cast<ContentSettingsType>(last_selected_page_.GetValue());
    if (page == CONTENT_SETTINGS_TYPE_DEFAULT)
      page = CONTENT_SETTINGS_TYPE_COOKIES;
  }
  // If the page number is out of bounds, reset to the first tab.
  if (page < 0 || page >= gtk_notebook_get_n_pages(GTK_NOTEBOOK(notebook_)))
    page = CONTENT_SETTINGS_TYPE_COOKIES;

  gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook_), page);
}

void ContentSettingsWindowGtk::OnSwitchPage(
    GtkWidget* notebook,
    GtkNotebookPage* page,
    guint page_num) {
  int index = page_num;
  DCHECK(index > CONTENT_SETTINGS_TYPE_DEFAULT &&
         index < CONTENT_SETTINGS_NUM_TYPES);
  last_selected_page_.SetValue(index);
}

void ContentSettingsWindowGtk::OnWindowDestroy(GtkWidget* widget) {
  settings_window = NULL;
  MessageLoop::current()->DeleteSoon(FROM_HERE, this);
}
<commit_msg>GTK: Make content options window non-resizable.<commit_after>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/gtk/options/content_settings_window_gtk.h"

#include <string>

#include "app/l10n_util.h"
#include "base/command_line.h"
#include "base/message_loop.h"
#include "chrome/browser/browser.h"
#include "chrome/browser/browser_list.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/gtk/accessible_widget_helper_gtk.h"
#include "chrome/browser/gtk/browser_window_gtk.h"
#include "chrome/browser/gtk/gtk_util.h"
#include "chrome/browser/pref_service.h"
#include "chrome/browser/profile.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/content_settings_types.h"
#include "chrome/common/notification_service.h"
#include "chrome/common/pref_names.h"
#include "grit/chromium_strings.h"
#include "grit/generated_resources.h"
#include "grit/locale_settings.h"

namespace {

// The singleton options window object.
ContentSettingsWindowGtk* settings_window = NULL;

}  // namespace

// static
void ContentSettingsWindowGtk::Show(GtkWindow* parent,
                                    ContentSettingsType page,
                                    Profile* profile) {
  DCHECK(profile);

  if (!settings_window) {
    // Creating and initializing a bunch of controls generates a bunch of
    // spurious events as control values change. Temporarily suppress
    // accessibility events until the window is created.
    profile->PauseAccessibilityEvents();

    // Create the options window.
    settings_window = new ContentSettingsWindowGtk(parent, profile);

    // Resume accessibility events.
    profile->ResumeAccessibilityEvents();
  }
  settings_window->ShowContentSettingsTab(page);
}

ContentSettingsWindowGtk::ContentSettingsWindowGtk(GtkWindow* parent,
                                                   Profile* profile)
    : profile_(profile),
      cookie_page_(profile),
      image_page_(profile, CONTENT_SETTINGS_TYPE_IMAGES),
      javascript_page_(profile, CONTENT_SETTINGS_TYPE_JAVASCRIPT),
      plugin_page_(profile, CONTENT_SETTINGS_TYPE_PLUGINS),
      popup_page_(profile, CONTENT_SETTINGS_TYPE_POPUPS),
      geolocation_page_(profile, CONTENT_SETTINGS_TYPE_GEOLOCATION) {
  // We don't need to observe changes in this value.
  last_selected_page_.Init(prefs::kContentSettingsWindowLastTabIndex,
                           profile->GetPrefs(), NULL);

  std::string dialog_name = l10n_util::GetStringUTF8(
      IDS_CONTENT_SETTINGS_TITLE);
  dialog_ = gtk_dialog_new_with_buttons(
      dialog_name.c_str(),
      parent,
      static_cast<GtkDialogFlags>(GTK_DIALOG_MODAL | GTK_DIALOG_NO_SEPARATOR),
      GTK_STOCK_CLOSE,
      GTK_RESPONSE_CLOSE,
      NULL);
  gtk_window_set_policy(GTK_WINDOW(dialog_), FALSE, FALSE, TRUE);

  accessible_widget_helper_.reset(new AccessibleWidgetHelper(
      dialog_, profile_));
  accessible_widget_helper_->SendOpenWindowNotification(dialog_name);

  gtk_window_set_default_size(GTK_WINDOW(dialog_), 500, -1);
  // Allow browser windows to go in front of the options dialog in metacity.
  gtk_window_set_type_hint(GTK_WINDOW(dialog_), GDK_WINDOW_TYPE_HINT_NORMAL);
  gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(dialog_)->vbox),
                      gtk_util::kContentAreaSpacing);
  gtk_util::SetWindowIcon(GTK_WINDOW(dialog_));

  notebook_ = gtk_notebook_new();

  gtk_notebook_append_page(
      GTK_NOTEBOOK(notebook_),
      cookie_page_.get_page_widget(),
      gtk_label_new(
          l10n_util::GetStringUTF8(IDS_COOKIES_TAB_LABEL).c_str()));
  gtk_notebook_append_page(
      GTK_NOTEBOOK(notebook_),
      image_page_.get_page_widget(),
      gtk_label_new(
          l10n_util::GetStringUTF8(IDS_IMAGES_TAB_LABEL).c_str()));
  gtk_notebook_append_page(
      GTK_NOTEBOOK(notebook_),
      javascript_page_.get_page_widget(),
      gtk_label_new(
          l10n_util::GetStringUTF8(IDS_JAVASCRIPT_TAB_LABEL).c_str()));
  gtk_notebook_append_page(
      GTK_NOTEBOOK(notebook_),
      plugin_page_.get_page_widget(),
      gtk_label_new(
          l10n_util::GetStringUTF8(IDS_PLUGIN_TAB_LABEL).c_str()));
  gtk_notebook_append_page(
      GTK_NOTEBOOK(notebook_),
      popup_page_.get_page_widget(),
      gtk_label_new(
          l10n_util::GetStringUTF8(IDS_POPUP_TAB_LABEL).c_str()));
  gtk_notebook_append_page(
      GTK_NOTEBOOK(notebook_),
      geolocation_page_.get_page_widget(),
      gtk_label_new(
          l10n_util::GetStringUTF8(IDS_GEOLOCATION_TAB_LABEL).c_str()));

  gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog_)->vbox), notebook_);

  DCHECK_EQ(gtk_notebook_get_n_pages(GTK_NOTEBOOK(notebook_)),
            CONTENT_SETTINGS_NUM_TYPES);

  // Need to show the notebook before connecting switch-page signal, otherwise
  // we'll immediately get a signal switching to page 0 and overwrite our
  // last_selected_page_ value.
  gtk_widget_show_all(dialog_);

  g_signal_connect(notebook_, "switch-page",
                   G_CALLBACK(OnSwitchPageThunk), this);

  // We only have one button and don't do any special handling, so just hook it
  // directly to gtk_widget_destroy.
  g_signal_connect(dialog_, "response", G_CALLBACK(gtk_widget_destroy), NULL);

  g_signal_connect(dialog_, "destroy", G_CALLBACK(OnWindowDestroyThunk), this);
}

ContentSettingsWindowGtk::~ContentSettingsWindowGtk() {
}

void ContentSettingsWindowGtk::ShowContentSettingsTab(
    ContentSettingsType page) {
  if (Browser* b = BrowserList::GetLastActive()) {
    gtk_util::CenterOverWindow(GTK_WINDOW(dialog_),
                               b->window()->GetNativeHandle());
  }

  // Bring options window to front if it already existed and isn't already
  // in front
  gtk_window_present_with_time(GTK_WINDOW(dialog_),
                               gtk_get_current_event_time());

  if (page == CONTENT_SETTINGS_TYPE_DEFAULT) {
    // Remember the last visited page from local state.
    page = static_cast<ContentSettingsType>(last_selected_page_.GetValue());
    if (page == CONTENT_SETTINGS_TYPE_DEFAULT)
      page = CONTENT_SETTINGS_TYPE_COOKIES;
  }
  // If the page number is out of bounds, reset to the first tab.
  if (page < 0 || page >= gtk_notebook_get_n_pages(GTK_NOTEBOOK(notebook_)))
    page = CONTENT_SETTINGS_TYPE_COOKIES;

  gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook_), page);
}

void ContentSettingsWindowGtk::OnSwitchPage(
    GtkWidget* notebook,
    GtkNotebookPage* page,
    guint page_num) {
  int index = page_num;
  DCHECK(index > CONTENT_SETTINGS_TYPE_DEFAULT &&
         index < CONTENT_SETTINGS_NUM_TYPES);
  last_selected_page_.SetValue(index);
}

void ContentSettingsWindowGtk::OnWindowDestroy(GtkWidget* widget) {
  settings_window = NULL;
  MessageLoop::current()->DeleteSoon(FROM_HERE, this);
}
<|endoftext|>