<commit_before>/* dlvhex -- Answer-Set Programming with external interfaces.
 * Copyright (C) 2005-2007 Roman Schindlauer
 * Copyright (C) 2006-2015 Thomas Krennwallner
 * Copyright (C) 2009-2015 Peter Schüller
 * Copyright (C) 2011-2015 Christoph Redl
 *
 * This file is part of dlvhex.
 *
 * dlvhex is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * dlvhex is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with dlvhex; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA.
 */

/**
 * @file   Benchmarking.cpp
 * @author Peter Schüller
 * @date   Sat Nov  5 15:26:18 CET 2005
 *
 * @brief  Benchmarking features (implementation).
 *
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif // HAVE_CONFIG_H

#include "dlvhex2/Benchmarking.h"
#include <boost/foreach.hpp>
#include <iostream>
#include <set>
#include <boost/thread/mutex.hpp>

DLVHEX_NAMESPACE_BEGIN

namespace benchmark
{

namespace simple
{

BenchmarkController::Stat::Stat(const std::string& name):
  name(name), count(0), prints(0), start(), duration(), running(false)
{
}

// init, display start of benchmarking
BenchmarkController::BenchmarkController():
  myID(0), maxID(0), instrumentations(), name2id(), output(&(std::cerr)), printSkip(0), sus(0)
{
  myID = getInstrumentationID("BenchmarkController lifetime");
  start(myID);
}

// destruct, output benchmark results
BenchmarkController::~BenchmarkController()
{
  stop(myID);

  BOOST_FOREACH(const Stat& st, instrumentations)
  {
    printInformation(st);
  }
}

namespace
{
BenchmarkController* instance = 0;
}

void BenchmarkController::finish()
{
  if( instance )
    delete instance;
  instance = 0;
}

BenchmarkController& BenchmarkController::Instance()
{
  if( instance == 0 )
    instance = new BenchmarkController;
  return *instance;
}

// output stream
void BenchmarkController::setOutput(std::ostream* o)
{
  output = o;
}

// amount of accumulated output (default: each call)
void BenchmarkController::setPrintInterval(Count skip)
{
  printSkip = skip;
}

// get ID or register new one
ID BenchmarkController::getInstrumentationID(const std::string& name)
{
  boost::mutex::scoped_lock lock(mutex);
  std::map<std::string, ID>::const_iterator it = name2id.find(name);
  if( it == name2id.end() )
  {
    ID newid = maxID;
    instrumentations.push_back(Stat(name));
    name2id[name] = newid;
    maxID++;
    return newid;
  }
  else
  {
    return it->second;
  }
}

void BenchmarkController::suspend(){
  if( sus == 0 ) {
    myID = getInstrumentationID("BMController suspend");
    start(myID);
  }
  boost::mutex::scoped_lock lock(mutex);
  sus++;
}

void BenchmarkController::resume(){
  boost::mutex::scoped_lock lock(mutex);
  sus--;
  if( sus == 0 ){
    myID = getInstrumentationID("BMController suspend");
    stop(myID);
  }
}

std::string BenchmarkController::count(const std::string& name, int width) const
{
  if (sus > 0) return "-";
  std::map<std::string, ID>::const_iterator it = name2id.find(name);
  if( it == name2id.end() )
    return "-";
  benchmark::ID id = it->second;
  std::ostringstream oss;
  oss << std::setw(width) << getStat(id).count;
  return oss.str();
}

std::string BenchmarkController::duration(const std::string& name, int width) const
{
  if (sus > 0) return "-";
  std::map<std::string, ID>::const_iterator it = name2id.find(name);
  if( it == name2id.end() )
    return "-";
  benchmark::ID id = it->second;
  std::ostringstream oss;
  printInSecs(oss, getStat(id).duration, width);
  return oss.str();
}

// stop and do not record, handle non-started id's gracefully
void BenchmarkController::invalidate(ID id)
{
  if (sus > 0) return;
  boost::mutex::scoped_lock lock(mutex);
  Stat& st = instrumentations[id];

  if( st.running )
    st.running = false;
}

// copy data from one id to another id and call stop() on that other id
// e.g. do this for several interesting benchmarks at first model
void BenchmarkController::snapshot(ID id, ID intoID)
{
  if (sus > 0) return;
	{
	  boost::mutex::scoped_lock lock(mutex);
	  Stat& st = instrumentations[id];
	  Stat& intost = instrumentations[intoID];

	  // copy (overwrite old snapshot!)
	  // do not copy name!
	  intost.count = st.count;
	  // do not copy prints! (might produce duplicates)
	  intost.start = st.start;
	  intost.duration = st.duration;
	  intost.running = st.running;
	}
	// stop but do not count
	stop(intoID, false);
}

void BenchmarkController::snapshot(const std::string& fromstr, const std::string& tostr)
{
  if (sus > 0) return;
	ID idfrom = getInstrumentationID(fromstr);
	ID idto = getInstrumentationID(tostr);
	snapshot(idfrom, idto);
}

} // namespace simple

namespace nestingAware
{

NestingAwareController::Stat::Stat(const std::string& name, Duration printInterval):
  name(name), count(0), duration(), pureDuration(), level(0),
  nextPrint(boost::posix_time::microsec_clock::local_time() + printInterval) {
}

NestingAwareController::Current::Current(ID which):
  which(which), firststart(), start() {}

// init, display start of benchmarking
NestingAwareController::NestingAwareController():
  myID(0), maxID(0), instrumentations(), name2id(), current(), output(&(std::cerr)),
  printInterval(boost::posix_time::seconds(10.0)) // print continuously all 10 seconds
{
  myID = getInstrumentationID("BenchmarkController lifetime");
  start(myID);
}

// destruct, output benchmark results
NestingAwareController::~NestingAwareController()
{
  stop(myID);
  if( !current.empty() && output )
    // better not throw from destructor
    (*output) << "destructing NestingAwareController but current is not empty!" << std::endl;

  // sort by pure duration, descending
  class PureSortPredicate {
    public:
      static bool isHigher(const Stat& s1, const Stat& s2) {
        return s1.pureDuration > s2.pureDuration;
      }
  };
  std::sort(instrumentations.begin(), instrumentations.end(), PureSortPredicate::isHigher);

  Duration total;
  BOOST_FOREACH(const Stat& st, instrumentations)
  {
    printInformation(st);
    if( st.count != 0 ) // this way we do not count snapshot "... to first model" pure durations
      total += st.pureDuration;
  }
  #ifndef NDEBUG
  // to verify if all partial times sum up to the total runtime
  if( output ) {
    (*output) << "Sum of pure durations = ";
    printInSecs(std::cerr, total) << "s." << std::endl;
  }
  #endif
}

namespace
{
NestingAwareController* instance = 0;
}

void NestingAwareController::finish()
{
  if( instance )
    delete instance;
  instance = 0;
}

NestingAwareController& NestingAwareController::Instance()
{
  if( instance == 0 )
    instance = new NestingAwareController;
  return *instance;
}

// output stream
void NestingAwareController::setOutput(std::ostream* o)
{
  output = o;
}

// amount of accumulated output (default: each call)
void NestingAwareController::setPrintInterval(Count skip)
{
  // TODO
  std::cerr << "not implemented NestingAwareController::setPrintInterval" << std::endl;
}

// get ID or register new one
ID NestingAwareController::getInstrumentationID(const std::string& name)
{
  boost::mutex::scoped_lock lock(mutex);
  std::map<std::string, ID>::const_iterator it = name2id.find(name);
  if( it == name2id.end() )
  {
    ID newid = maxID;
    instrumentations.push_back(Stat(name, printInterval));
    name2id[name] = newid;
    maxID++;
    return newid;
  }
  else
  {
    return it->second;
  }
}

void NestingAwareController::suspend(){
  if( sus == 0 ) {
    myID = getInstrumentationID("BMController suspend");
    start(myID);
  }
  boost::mutex::scoped_lock lock(mutex);
  sus++;
}

void NestingAwareController::resume(){
  boost::mutex::scoped_lock lock(mutex);
  sus--;
  if( sus == 0 ){
    myID = getInstrumentationID("BMController suspend");
    stop(myID);
  }
}

std::string NestingAwareController::count(const std::string& name, int width) const
{
  std::map<std::string, ID>::const_iterator it = name2id.find(name);
  if( it == name2id.end() )
    return "-";
  benchmark::ID id = it->second;
  std::ostringstream oss;
  oss << std::setw(width) << getStat(id).count;
  return oss.str();
}

std::string NestingAwareController::duration(const std::string& name, int width) const
{
  std::map<std::string, ID>::const_iterator it = name2id.find(name);
  if( it == name2id.end() )
    return "-";
  benchmark::ID id = it->second;
  std::ostringstream oss;
  // we return pure duration as it is the purpose of NestingAwareController
  printInSecs(oss, getStat(id).pureDuration, width);
  return oss.str();
}

// stop and do not record, handle non-started id's gracefully
void NestingAwareController::invalidate(ID id)
{
  if( !current.empty() && current.back().which == id ) {
    // save start time of pure period (we do not want to lose this)
    Time start = current.back().start;
    // destroy current without counting it
    current.pop_back();
    // set start time to new top-of-stack
    if( current.size() > 1 ) {
      current[current.size()-2].start = start;
    }
  }
}

// copy data from one id to another id and call stop() on that other id
// e.g. do this for several interesting benchmarks at first model
void NestingAwareController::snapshot(ID id, ID intoID)
{
  boost::mutex::scoped_lock lock(mutex);
  Stat& st = instrumentations[id];
  Stat& intost = instrumentations[intoID];

  // copy (overwrites old snapshot!)

  Time now = boost::posix_time::microsec_clock::local_time();
  intost.count = st.count;
  intost.duration = st.duration;
  intost.pureDuration = st.pureDuration;
  if( current.back().which == id ) {
    // if top level entry in current has which=id then add to pureDuration
    intost.pureDuration += now - current.back().start;
  }
  // find bottom-most level entry in current where which=id and add to duration (if exists)
  for(unsigned u = 0; u < current.size(); ++u) {
    if( current[u].which == id ) {
      intost.duration += now - current[u].firststart;
      break;
    }
  }
}

// print information about stat
void NestingAwareController::printInformation(const Stat& st)
{
  if( output )
  {
    (*output) <<
      "BM:" << // std::setw(2) << int(&st-instrumentations.data()) << " " <<
      std::setw(30) << st.name <<
      ": count:" << std::setw(8) << st.count;
		(*output) << " total:";
    printInSecs(*output, st.duration, 4) << "s pure:";
    printInSecs(*output, st.pureDuration, 4) << "s avg:";
		if( st.count > 0 )
		{
			printInSecs(*output, st.duration/st.count, 4) << "s";
		}
		else
		{
			(*output) << "  -.---s";
		}
    (*output) << std::endl;
  }
}

void NestingAwareController::snapshot(const std::string& fromstr, const std::string& tostr)
{
	ID idfrom = getInstrumentationID(fromstr);
	ID idto = getInstrumentationID(tostr);
	snapshot(idfrom, idto);
}

} // namespace simple

} // namespace benchmark

DLVHEX_NAMESPACE_END

// Local Variables:
// mode: C++
// End:
<commit_msg>showing pure time percentages<commit_after>/* dlvhex -- Answer-Set Programming with external interfaces.
 * Copyright (C) 2005-2007 Roman Schindlauer
 * Copyright (C) 2006-2015 Thomas Krennwallner
 * Copyright (C) 2009-2015 Peter Schüller
 * Copyright (C) 2011-2015 Christoph Redl
 *
 * This file is part of dlvhex.
 *
 * dlvhex is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * dlvhex is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with dlvhex; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA.
 */

/**
 * @file   Benchmarking.cpp
 * @author Peter Schüller
 * @date   Sat Nov  5 15:26:18 CET 2005
 *
 * @brief  Benchmarking features (implementation).
 *
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif // HAVE_CONFIG_H

#include "dlvhex2/Benchmarking.h"
#include <boost/foreach.hpp>
#include <iostream>
#include <set>
#include <boost/thread/mutex.hpp>

DLVHEX_NAMESPACE_BEGIN

namespace benchmark
{

namespace simple
{

BenchmarkController::Stat::Stat(const std::string& name):
  name(name), count(0), prints(0), start(), duration(), running(false)
{
}

// init, display start of benchmarking
BenchmarkController::BenchmarkController():
  myID(0), maxID(0), instrumentations(), name2id(), output(&(std::cerr)), printSkip(0), sus(0)
{
  myID = getInstrumentationID("BenchmarkController lifetime");
  start(myID);
}

// destruct, output benchmark results
BenchmarkController::~BenchmarkController()
{
  stop(myID);

  BOOST_FOREACH(const Stat& st, instrumentations)
  {
    printInformation(st);
  }
}

namespace
{
BenchmarkController* instance = 0;
}

void BenchmarkController::finish()
{
  if( instance )
    delete instance;
  instance = 0;
}

BenchmarkController& BenchmarkController::Instance()
{
  if( instance == 0 )
    instance = new BenchmarkController;
  return *instance;
}

// output stream
void BenchmarkController::setOutput(std::ostream* o)
{
  output = o;
}

// amount of accumulated output (default: each call)
void BenchmarkController::setPrintInterval(Count skip)
{
  printSkip = skip;
}

// get ID or register new one
ID BenchmarkController::getInstrumentationID(const std::string& name)
{
  boost::mutex::scoped_lock lock(mutex);
  std::map<std::string, ID>::const_iterator it = name2id.find(name);
  if( it == name2id.end() )
  {
    ID newid = maxID;
    instrumentations.push_back(Stat(name));
    name2id[name] = newid;
    maxID++;
    return newid;
  }
  else
  {
    return it->second;
  }
}

void BenchmarkController::suspend(){
  if( sus == 0 ) {
    myID = getInstrumentationID("BMController suspend");
    start(myID);
  }
  boost::mutex::scoped_lock lock(mutex);
  sus++;
}

void BenchmarkController::resume(){
  boost::mutex::scoped_lock lock(mutex);
  sus--;
  if( sus == 0 ){
    myID = getInstrumentationID("BMController suspend");
    stop(myID);
  }
}

std::string BenchmarkController::count(const std::string& name, int width) const
{
  if (sus > 0) return "-";
  std::map<std::string, ID>::const_iterator it = name2id.find(name);
  if( it == name2id.end() )
    return "-";
  benchmark::ID id = it->second;
  std::ostringstream oss;
  oss << std::setw(width) << getStat(id).count;
  return oss.str();
}

std::string BenchmarkController::duration(const std::string& name, int width) const
{
  if (sus > 0) return "-";
  std::map<std::string, ID>::const_iterator it = name2id.find(name);
  if( it == name2id.end() )
    return "-";
  benchmark::ID id = it->second;
  std::ostringstream oss;
  printInSecs(oss, getStat(id).duration, width);
  return oss.str();
}

// stop and do not record, handle non-started id's gracefully
void BenchmarkController::invalidate(ID id)
{
  if (sus > 0) return;
  boost::mutex::scoped_lock lock(mutex);
  Stat& st = instrumentations[id];

  if( st.running )
    st.running = false;
}

// copy data from one id to another id and call stop() on that other id
// e.g. do this for several interesting benchmarks at first model
void BenchmarkController::snapshot(ID id, ID intoID)
{
  if (sus > 0) return;
	{
	  boost::mutex::scoped_lock lock(mutex);
	  Stat& st = instrumentations[id];
	  Stat& intost = instrumentations[intoID];

	  // copy (overwrite old snapshot!)
	  // do not copy name!
	  intost.count = st.count;
	  // do not copy prints! (might produce duplicates)
	  intost.start = st.start;
	  intost.duration = st.duration;
	  intost.running = st.running;
	}
	// stop but do not count
	stop(intoID, false);
}

void BenchmarkController::snapshot(const std::string& fromstr, const std::string& tostr)
{
  if (sus > 0) return;
	ID idfrom = getInstrumentationID(fromstr);
	ID idto = getInstrumentationID(tostr);
	snapshot(idfrom, idto);
}

} // namespace simple

namespace nestingAware
{

NestingAwareController::Stat::Stat(const std::string& name, Duration printInterval):
  name(name), count(0), duration(), pureDuration(), level(0),
  nextPrint(boost::posix_time::microsec_clock::local_time() + printInterval) {
}

NestingAwareController::Current::Current(ID which):
  which(which), firststart(), start() {}

// init, display start of benchmarking
NestingAwareController::NestingAwareController():
  myID(0), maxID(0), instrumentations(), name2id(), current(), output(&(std::cerr)),
  printInterval(boost::posix_time::seconds(10.0)) // print continuously all 10 seconds
{
  myID = getInstrumentationID("BenchmarkController lifetime");
  start(myID);
}

// destruct, output benchmark results
NestingAwareController::~NestingAwareController()
{
  stop(myID);
  if( !output )
    return;

  if( !current.empty() )
    // better not throw from destructor
    (*output) << "destructing NestingAwareController but current is not empty!" << std::endl;

  // sort by pure duration, descending
  class PureSortPredicate {
    public:
      static bool isHigher(const Stat& s1, const Stat& s2) {
        return s1.pureDuration > s2.pureDuration;
      }
  };
  std::sort(instrumentations.begin(), instrumentations.end(), PureSortPredicate::isHigher);

  // compute total pure duration
  // (this is necessary to compute relative durations; relative durations are useful
  //  in ASP applications with user interaction where no two runs are the same)
  Duration total;
  BOOST_FOREACH(const Stat& st, instrumentations)
  {
    if( st.count != 0 ) // this way we do not count snapshot "... to first model" pure durations
      total += st.pureDuration;
  }

  // print sorted summary
  BOOST_FOREACH(const Stat& st, instrumentations)
  {
    (*output) <<
      "BM:" << // std::setw(2) << int(&st-instrumentations.data()) << " " <<
      std::setw(30) << st.name <<
      ": count:" << std::setw(8) << st.count;
    (*output) << " total:";
    printInSecs(*output, st.duration, 4) << "s pure:";
    printInSecs(*output, st.pureDuration, 4) << "s";
    if( st.count > 0 )
    {
      float ratio = (1000L * st.pureDuration.total_milliseconds() / total.total_milliseconds())/1000.0;
      (*output) << " (" << std::setw(4) << std::setprecision(1) << std::fixed << (100.0*ratio) << "%)" <<
      " avg:";
      printInSecs(*output, st.duration/st.count, 4) << "s";
    }
    (*output) << std::endl;
  }

  #ifndef NDEBUG
  // to verify if all partial times sum up to the total runtime
  if( output ) {
    (*output) << "Sum of pure durations = ";
    printInSecs(std::cerr, total) << "s." << std::endl;
  }
  #endif
}

namespace
{
NestingAwareController* instance = 0;
}

void NestingAwareController::finish()
{
  if( instance )
    delete instance;
  instance = 0;
}

NestingAwareController& NestingAwareController::Instance()
{
  if( instance == 0 )
    instance = new NestingAwareController;
  return *instance;
}

// output stream
void NestingAwareController::setOutput(std::ostream* o)
{
  output = o;
}

// amount of accumulated output (default: each call)
void NestingAwareController::setPrintInterval(Count skip)
{
  // TODO
  std::cerr << "not implemented NestingAwareController::setPrintInterval" << std::endl;
}

// get ID or register new one
ID NestingAwareController::getInstrumentationID(const std::string& name)
{
  boost::mutex::scoped_lock lock(mutex);
  std::map<std::string, ID>::const_iterator it = name2id.find(name);
  if( it == name2id.end() )
  {
    ID newid = maxID;
    instrumentations.push_back(Stat(name, printInterval));
    name2id[name] = newid;
    maxID++;
    return newid;
  }
  else
  {
    return it->second;
  }
}

void NestingAwareController::suspend(){
  if( sus == 0 ) {
    myID = getInstrumentationID("BMController suspend");
    start(myID);
  }
  boost::mutex::scoped_lock lock(mutex);
  sus++;
}

void NestingAwareController::resume(){
  boost::mutex::scoped_lock lock(mutex);
  sus--;
  if( sus == 0 ){
    myID = getInstrumentationID("BMController suspend");
    stop(myID);
  }
}

std::string NestingAwareController::count(const std::string& name, int width) const
{
  std::map<std::string, ID>::const_iterator it = name2id.find(name);
  if( it == name2id.end() )
    return "-";
  benchmark::ID id = it->second;
  std::ostringstream oss;
  oss << std::setw(width) << getStat(id).count;
  return oss.str();
}

std::string NestingAwareController::duration(const std::string& name, int width) const
{
  std::map<std::string, ID>::const_iterator it = name2id.find(name);
  if( it == name2id.end() )
    return "-";
  benchmark::ID id = it->second;
  std::ostringstream oss;
  // we return pure duration as it is the purpose of NestingAwareController
  printInSecs(oss, getStat(id).pureDuration, width);
  return oss.str();
}

// stop and do not record, handle non-started id's gracefully
void NestingAwareController::invalidate(ID id)
{
  if( !current.empty() && current.back().which == id ) {
    // save start time of pure period (we do not want to lose this)
    Time start = current.back().start;
    // destroy current without counting it
    current.pop_back();
    // set start time to new top-of-stack
    if( current.size() > 1 ) {
      current[current.size()-2].start = start;
    }
  }
}

// copy data from one id to another id and call stop() on that other id
// e.g. do this for several interesting benchmarks at first model
void NestingAwareController::snapshot(ID id, ID intoID)
{
  boost::mutex::scoped_lock lock(mutex);
  Stat& st = instrumentations[id];
  Stat& intost = instrumentations[intoID];

  // copy (overwrites old snapshot!)

  Time now = boost::posix_time::microsec_clock::local_time();
  intost.count = st.count;
  intost.duration = st.duration;
  intost.pureDuration = st.pureDuration;
  if( current.back().which == id ) {
    // if top level entry in current has which=id then add to pureDuration
    intost.pureDuration += now - current.back().start;
  }
  // find bottom-most level entry in current where which=id and add to duration (if exists)
  for(unsigned u = 0; u < current.size(); ++u) {
    if( current[u].which == id ) {
      intost.duration += now - current[u].firststart;
      break;
    }
  }
}

// print information about stat
void NestingAwareController::printInformation(const Stat& st)
{
  if( output )
  {
    (*output) <<
      "BM:" << // std::setw(2) << int(&st-instrumentations.data()) << " " <<
      std::setw(30) << st.name <<
      ": count:" << std::setw(8) << st.count;
		(*output) << " total:";
    printInSecs(*output, st.duration, 4) << "s pure:";
    printInSecs(*output, st.pureDuration, 4) << "s avg:";
		if( st.count > 0 )
		{
			printInSecs(*output, st.duration/st.count, 4) << "s";
		}
		else
		{
			(*output) << "  -.---s";
		}
    (*output) << std::endl;
  }
}

void NestingAwareController::snapshot(const std::string& fromstr, const std::string& tostr)
{
	ID idfrom = getInstrumentationID(fromstr);
	ID idto = getInstrumentationID(tostr);
	snapshot(idfrom, idto);
}

} // namespace simple

} // namespace benchmark

DLVHEX_NAMESPACE_END

// Local Variables:
// mode: C++
// End:
<|endoftext|>


<commit_before><commit_msg>Reverting r14043, r14188. The fix that I'm reverting needs to be done, but we need to come up with a clean implementation complete with documentation about what the input methods are that we're explicitly testing for.<commit_after><|endoftext|>


<commit_before>//==============================================================================
// Copyright (c) 2016 Advanced Micro Devices, Inc. All rights reserved.
/// \author AMD Developer Tools Team
/// \file
/// \brief  HSA Agent (tools lib) implementation
//==============================================================================

#include <cstdlib>
#include <cstring>

#include <hsa_api_trace.h>

#include "HSAAPITable1_0.h"
#include "GPUPerfAPIHSAGlobals.h"

#ifndef GPADLL_EXPORT
    #if defined(_WIN32) || defined(__CYGWIN__)
        #ifdef __GNUC__
            #define GPADLL_EXPORT __attribute__ ((dllexport))
        #else
            #define GPADLL_EXPORT __declspec(dllexport)
        #endif
    #else
        #if __GNUC__ >= 4
            #define GPADLL_EXPORT __attribute__ ((visibility ("default")))
        #else
            #define GPADLL_EXPORT
        #endif
    #endif
#endif //DLL_EXPORT

decltype(hsa_queue_create)* g_realQueueCreateFn = nullptr;

std::map<hsa_queue_t*, hsa_agent_t> g_queueAgentMap; ///< typedef for a map from queue to agent

/// replacement function for hsa_queue_create
hsa_status_t HSA_API
my_hsa_queue_create(hsa_agent_t agent, uint32_t size, hsa_queue_type_t type,
                    void(*pCallback)(hsa_status_t status, hsa_queue_t* pSource, void* pData),
                    void* pData, uint32_t private_segment_size,
                    uint32_t group_segment_size, hsa_queue_t** ppQueue)
{
    hsa_status_t retVal = g_realQueueCreateFn(agent, size, type, pCallback, pData, private_segment_size, group_segment_size, ppQueue);

    if (HSA_STATUS_SUCCESS == retVal)
    {
        HSAGlobalFlags::Instance()->m_queueAgentMap[*ppQueue] = agent;
    }

    return retVal;
}

/// exported function called when tools libs are loaded
extern "C" bool GPADLL_EXPORT OnLoad(void* pTable, uint64_t runtimeVersion, uint64_t /*failedToolCount*/, const char* const* /*pFailedToolNames*/)
{

    if (0 == runtimeVersion)
    {
        g_realQueueCreateFn = reinterpret_cast<ApiTable1_0*>(pTable)->hsa_queue_create_fn;
        reinterpret_cast<ApiTable1_0*>(pTable)->hsa_queue_create_fn = my_hsa_queue_create;
    }
    else
    {
        g_realQueueCreateFn = reinterpret_cast<HsaApiTable*>(pTable)->core_->hsa_queue_create_fn;
        reinterpret_cast<HsaApiTable*>(pTable)->core_->hsa_queue_create_fn = my_hsa_queue_create;
    }

    return true;
}

/// exported function called when tools libs are unloaded
extern "C" void GPADLL_EXPORT OnUnload()
{
}
<commit_msg>Adapt to ROCm 1.5<commit_after>//==============================================================================
// Copyright (c) 2016 Advanced Micro Devices, Inc. All rights reserved.
/// \author AMD Developer Tools Team
/// \file
/// \brief  HSA Agent (tools lib) implementation
//==============================================================================

#include <cstdlib>
#include <cstring>

#include <hsa_api_trace.h>

#include "HSAAPITable1_0.h"
#include "GPUPerfAPIHSAGlobals.h"

#ifndef GPADLL_EXPORT
    #if defined(_WIN32) || defined(__CYGWIN__)
        #ifdef __GNUC__
            #define GPADLL_EXPORT __attribute__ ((dllexport))
        #else
            #define GPADLL_EXPORT __declspec(dllexport)
        #endif
    #else
        #if __GNUC__ >= 4
            #define GPADLL_EXPORT __attribute__ ((visibility ("default")))
        #else
            #define GPADLL_EXPORT
        #endif
    #endif
#endif //DLL_EXPORT

decltype(hsa_queue_create)* g_realQueueCreateFn = nullptr;

std::map<hsa_queue_t*, hsa_agent_t> g_queueAgentMap; ///< typedef for a map from queue to agent

/// replacement function for hsa_queue_create
hsa_status_t HSA_API
my_hsa_queue_create(hsa_agent_t agent, uint32_t size, hsa_queue_type32_t type,
                    void(*pCallback)(hsa_status_t status, hsa_queue_t* pSource, void* pData),
                    void* pData, uint32_t private_segment_size,
                    uint32_t group_segment_size, hsa_queue_t** ppQueue)
{
    hsa_status_t retVal = g_realQueueCreateFn(agent, size, type, pCallback, pData, private_segment_size, group_segment_size, ppQueue);

    if (HSA_STATUS_SUCCESS == retVal)
    {
        HSAGlobalFlags::Instance()->m_queueAgentMap[*ppQueue] = agent;
    }

    return retVal;
}

/// exported function called when tools libs are loaded
extern "C" bool GPADLL_EXPORT OnLoad(void* pTable, uint64_t runtimeVersion, uint64_t /*failedToolCount*/, const char* const* /*pFailedToolNames*/)
{

    if (0 == runtimeVersion)
    {
        g_realQueueCreateFn = reinterpret_cast<ApiTable1_0*>(pTable)->hsa_queue_create_fn;
        reinterpret_cast<ApiTable1_0*>(pTable)->hsa_queue_create_fn = my_hsa_queue_create;
    }
    else
    {
        g_realQueueCreateFn = reinterpret_cast<HsaApiTable*>(pTable)->core_->hsa_queue_create_fn;
        reinterpret_cast<HsaApiTable*>(pTable)->core_->hsa_queue_create_fn = my_hsa_queue_create;
    }

    return true;
}

/// exported function called when tools libs are unloaded
extern "C" void GPADLL_EXPORT OnUnload()
{
}
<|endoftext|>


<commit_before>/***********************************************************************************************************************
**
** Copyright (c) 2011, 2016 ETH Zurich
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
** following conditions are met:
**
** * Redistributions of source code must retain the above copyright notice, this list of conditions and the following
** disclaimer.
** * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
** following disclaimer in the documentation and/or other materials provided with the distribution.
** * Neither the name of the ETH Zurich nor the names of its contributors may be used to endorse or promote products
** derived from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
** INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
** DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
***********************************************************************************************************************/

#include "CClear.h"

#include "VersionControlUI/src/DiffManager.h"

using namespace Visualization;
using namespace FilePersistence;

namespace VersionControlUI {

CClear::CClear() : Command{"clear"} {}

bool CClear::canInterpret(Visualization::Item*, Visualization::Item*,
		const QStringList& commandTokens, const std::unique_ptr<Visualization::Cursor>& )
{
		// check that command name starts with the characters of the first token
		if (commandTokens.isEmpty() || !name().startsWith(commandTokens.first()))
			return false;

	return true;
}

Interaction::CommandResult* CClear::execute(Visualization::Item*, Visualization::Item*,
				const QStringList&, const std::unique_ptr<Visualization::Cursor>& )
{
	DiffManager::clear();

	return new Interaction::CommandResult{};
}

QList<Interaction::CommandSuggestion*> CClear::suggest(Visualization::Item*, Visualization::Item*,
		const QString& textSoFar, const std::unique_ptr<Visualization::Cursor>&)
{
	if (name().startsWith(textSoFar))
		return {new Interaction::CommandSuggestion{name(), "Clear current view from higlights "
																			"and scaling of diffs overview command"}};

	return {};
}

}
<commit_msg>Correct description of CClear command<commit_after>/***********************************************************************************************************************
**
** Copyright (c) 2011, 2016 ETH Zurich
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
** following conditions are met:
**
** * Redistributions of source code must retain the above copyright notice, this list of conditions and the following
** disclaimer.
** * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
** following disclaimer in the documentation and/or other materials provided with the distribution.
** * Neither the name of the ETH Zurich nor the names of its contributors may be used to endorse or promote products
** derived from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
** INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
** DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
***********************************************************************************************************************/

#include "CClear.h"

#include "VersionControlUI/src/DiffManager.h"

using namespace Visualization;
using namespace FilePersistence;

namespace VersionControlUI {

CClear::CClear() : Command{"clear"} {}

bool CClear::canInterpret(Visualization::Item*, Visualization::Item*,
		const QStringList& commandTokens, const std::unique_ptr<Visualization::Cursor>& )
{
		// check that command name starts with the characters of the first token
		if (commandTokens.isEmpty() || !name().startsWith(commandTokens.first()))
			return false;

	return true;
}

Interaction::CommandResult* CClear::execute(Visualization::Item*, Visualization::Item*,
				const QStringList&, const std::unique_ptr<Visualization::Cursor>& )
{
	DiffManager::clear();

	return new Interaction::CommandResult{};
}

QList<Interaction::CommandSuggestion*> CClear::suggest(Visualization::Item*, Visualization::Item*,
		const QString& textSoFar, const std::unique_ptr<Visualization::Cursor>&)
{
	if (name().startsWith(textSoFar))
		return {new Interaction::CommandSuggestion{name(), "Clear current view from diffs scaling and "
																			"overview commands highlights"}};

	return {};
}

}
<|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (C) 2014 Alexander Rössler
** License: LGPL version 2.1
**
** This file is part of QtQuickVcp.
**
** All rights reserved. This program and the accompanying materials
** are made available under the terms of the GNU Lesser General Public License
** (LGPL) version 2.1 which accompanies this distribution, and is available at
** http://www.gnu.org/licenses/lgpl-2.1.html
**
** This library is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
** Lesser General Public License for more details.
**
** Contributors:
** Alexander Rössler @ The Cool Tool GmbH <mail DOT aroessler AT gmail DOT com>
**
****************************************************************************/

#include "applicationfile.h"
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QCoreApplication>
#include <QFileInfo>
#include <QDir>

namespace qtquickvcp {

/*!
    \qmltype ApplicationFile
    \instantiates ApplicationFile
    \inqmlmodule Machinekit.Application
    \brief A remote file handler.
    \ingroup application

    This component interfaces with the Machinekit file service.
*/

/*! \property string ApplicationFile::localFilePath

    This property holds the path of the local file when loaded.
*/

/*! \property string ApplicationFile::localPath

    This property holds the path of the local directory for storing files.
*/

/*! \property string ApplicationFile::remoteFilePath

    This property holds the path of the remote file.

    The local file name is extracted from this property.
*/

/*! \property string ApplicationFile::remotePath

    This property holds the path of the remote directory for storing files.
*/

ApplicationFile::ApplicationFile(QObject *parent) :
    QObject(parent),
    m_uri(QLatin1String("")),
    m_localFilePath(QStringLiteral("file://")),
    m_remoteFilePath(QStringLiteral("file://")),
    m_localPath(QLatin1String("")),
    m_remotePath(QLatin1String("")),
    m_serverDirectory(QLatin1String("")),
    m_transferState(NoTransfer),
    m_error(NoError),
    m_errorString(QLatin1String("")),
    m_progress(0.0),
    m_networkReady(false),
    m_model(nullptr),
    m_ready(false),
    m_showHidden(false),
    m_networkManager(nullptr),
    m_file(nullptr),
    m_ftp(nullptr)
{
    m_temporaryDir = std::make_unique<QTemporaryDir>();
    m_temporaryDir->setAutoRemove(true);
    m_localPath = QUrl::fromLocalFile(m_temporaryDir->path());

    m_model = new ApplicationFileModel(this);

    m_networkManager = new QNetworkAccessManager(this);
    connect(m_networkManager, &QNetworkAccessManager::networkAccessibleChanged,
            this, &ApplicationFile::networkAccessibleChanged);

    if (m_networkManager->networkAccessible() == QNetworkAccessManager::Accessible)
    {
        initializeFtp();
    }
}

ApplicationFile::~ApplicationFile()
{
    cleanupFtp();
    cleanupFile();
    m_networkManager->deleteLater();
    m_model->deleteLater();
}

void ApplicationFile::startUpload()
{
    QUrl url;
    QFileInfo fileInfo(m_localFilePath.toLocalFile());
    QString remotePath;

    if (!ready() || (m_transferState != NoTransfer))
    {
        return;
    }

    url.setUrl(m_uri);
    remotePath = m_remotePath.toLocalFile();
    if (!m_serverDirectory.isEmpty())
    {
        remotePath.append("/");
        remotePath.append(m_serverDirectory);
    }
    m_remoteFilePath = QUrl::fromLocalFile(QDir(remotePath).filePath(fileInfo.fileName())).toString();
    emit remoteFilePathChanged(m_remoteFilePath.toString());

    m_file = std::make_unique<QFile>(fileInfo.filePath(), this);

    if (m_file->open(QIODevice::ReadOnly))
    {
        m_ftp->connectToHost(url.host(), static_cast<quint16>(url.port()));
        m_ftp->login();
        if (!m_serverDirectory.isEmpty())
        {
            m_ftp->cd(m_serverDirectory);
        }
        m_ftp->put(m_file.get(), fileInfo.fileName(), QFtp::Binary);
        m_ftp->close();

        m_progress = 0.0;
        emit progressChanged(m_progress);
        updateState(UploadRunning);
        updateError(NoError, QLatin1String(""));
    }
    else
    {
        updateState(Error);
        updateError(FileError, m_file->errorString());
    }
}

void ApplicationFile::startDownload()
{
    QUrl url;
    QDir dir;
    QString localFilePath;
    QString remoteFilePath;
    QString remotePath;
    QString fileName;

    if (!ready() || (m_transferState != NoTransfer))
    {
        return;
    }

    remoteFilePath = QDir::cleanPath(m_remoteFilePath.toLocalFile());
    remotePath = QDir::cleanPath(m_remotePath.toLocalFile());
    fileName = remoteFilePath.mid(remotePath.length() + 1);

    int i = fileName.indexOf(QLatin1String("/"));
    if (i > -1) // not in root directory
    {
        m_serverDirectory = fileName.left(i);
        emit serverDirectoryChanged(m_serverDirectory);
        fileName = fileName.mid(i + 1);
    }

    url.setUrl(m_uri);

    localFilePath = applicationFilePath(fileName, m_serverDirectory);
    m_localFilePath = QUrl::fromLocalFile(localFilePath);
    emit localFilePathChanged(m_localFilePath.toString());

    QFileInfo fileInfo(localFilePath);

    if (!dir.mkpath(fileInfo.absolutePath()))
    {
        updateState(Error);
        updateError(FileError, tr("Cannot create directory: %1").arg(fileInfo.absolutePath()));
        return;
    }

    m_file = std::make_unique<QFile>(localFilePath, this);

    if (m_file->open(QIODevice::WriteOnly))
    {
        m_ftp->connectToHost(url.host(), static_cast<quint16>(url.port()));
        m_ftp->login();
        if (!m_serverDirectory.isEmpty())
        {
            m_ftp->cd(m_serverDirectory);
        }
        m_ftp->get(fileName, m_file.get(), QFtp::Binary);
        m_ftp->close();
        m_progress = 0.0;
        emit progressChanged(m_progress);
        updateState(DownloadRunning);
        updateError(NoError, QLatin1String(""));
    }
    else
    {
        updateState(Error);
        updateError(FileError, m_file->errorString());
    }
}

void ApplicationFile::refreshFiles()
{
    QUrl url(m_uri);

    if (!ready() || (m_transferState != NoTransfer))
    {
        return;
    }

    m_model->clear();
    m_ftp->connectToHost(url.host(), static_cast<quint16>(url.port()));
    m_ftp->login();
    if (!m_serverDirectory.isEmpty())
    {
        m_ftp->cd(m_serverDirectory);
    }
    m_ftp->list();
    m_ftp->close();
    updateState(RefreshRunning);
}

void ApplicationFile::removeFile(const QString &name)
{
    QUrl url(m_uri);

    if (!ready() || (m_transferState != NoTransfer))
    {
        return;
    }

    m_ftp->connectToHost(url.host(), static_cast<quint16>(url.port()));
    m_ftp->login();
    if (!m_serverDirectory.isEmpty())
    {
        m_ftp->cd(m_serverDirectory);
    }
    m_ftp->remove(name);
    m_ftp->close();
    updateState(RemoveRunning);
}

void ApplicationFile::removeDirectory(const QString &name)
{
    QUrl url(m_uri);

    if (!ready() || (m_transferState != NoTransfer))
    {
        return;
    }

    m_ftp->connectToHost(url.host(), static_cast<quint16>(url.port()));
    m_ftp->login();
    if (!m_serverDirectory.isEmpty())
    {
        m_ftp->cd(m_serverDirectory);
    }
    m_ftp->rmdir(name);
    m_ftp->close();
    updateState(RemoveDirectoryRunning);
}

void ApplicationFile::createDirectory(const QString &name)
{
    QUrl url(m_uri);

    if (!ready() || (m_transferState != NoTransfer))
    {
        return;
    }

    m_ftp->connectToHost(url.host(), static_cast<quint16>(url.port()));
    m_ftp->login();
    if (!m_serverDirectory.isEmpty())
    {
        m_ftp->cd(m_serverDirectory);
    }
    m_ftp->mkdir(name);
    m_ftp->close();
    updateState(CreateDirectoryRunning);
}

void ApplicationFile::abort()
{
    if (!m_ftp)
    {
        return;
    }
    updateState(NoTransfer);
    m_ftp->abort();
    m_ftp->close();
}

void ApplicationFile::clearError()
{
    updateState(NoTransfer);
    updateError(NoError, QLatin1String(""));
}

void ApplicationFile::updateState(ApplicationFile::TransferState state)
{
    if (state != m_transferState)
    {
        m_transferState = state;
        emit transferStateChanged(m_transferState);
    }
}

void ApplicationFile::updateError(ApplicationFile::TransferError error, const QString &errorString)
{
    if (m_errorString != errorString)
    {
        m_errorString = errorString;
        emit errorStringChanged(m_errorString);
    }

    if (m_error != error)
    {
        m_error = error;
        emit errorChanged(m_error);
    }
}

QString ApplicationFile::applicationFilePath(const QString &fileName, const QString &serverDirectory) const
{
    const auto serverPath = QDir(m_localPath.toLocalFile()).filePath(serverDirectory);
    return QDir(serverPath).absoluteFilePath(fileName);
}

void ApplicationFile::initializeFtp()
{
    m_ftp = new QFtp(this);
    connect(m_ftp, &QFtp::commandFinished,
    this, &ApplicationFile::ftpCommandFinished);
    connect(m_ftp, &QFtp::listInfo,
    this, &ApplicationFile::addToList);
    connect(m_ftp, &QFtp::dataTransferProgress,
    this, &ApplicationFile::transferProgress);

    m_networkReady = true;
    emit readyChanged(m_networkReady);
}

void ApplicationFile::cleanupFtp()
{
    if (m_ftp == nullptr)
    {
        return;
    }

    m_ftp->abort();
    m_ftp->deleteLater();
    m_ftp = nullptr;

    m_networkReady = false;
    emit readyChanged(m_networkReady);
}

void ApplicationFile::cleanupFile()
{
    if (!m_file)
    {
        return;
    }

    m_file->close();
    m_file.release();
}

void ApplicationFile::transferProgress(qint64 bytesSent, qint64 bytesTotal)
{
    m_progress = static_cast<double>(bytesSent) / static_cast<double>(bytesTotal);
    emit progressChanged(m_progress);
}

void ApplicationFile::networkAccessibleChanged(QNetworkAccessManager::NetworkAccessibility accesible)
{
    if (accesible == QNetworkAccessManager::Accessible)
    {
        initializeFtp();
    }
    else
    {
        cleanupFtp();
    }
}

void ApplicationFile::addToList(const QUrlInfo &urlInfo)
{
    if (!m_showHidden && urlInfo.name().startsWith(QLatin1String("."))) {
        return;
    }

    auto item = new ApplicationFileItem();
    item->setName(urlInfo.name());
    item->setSize(urlInfo.size());
    item->setOwner(urlInfo.owner());
    item->setGroup(urlInfo.group());
    item->setLastModified(urlInfo.lastModified());
    item->setDir(urlInfo.isDir());

    m_model->addItem(item);
}

void ApplicationFile::ftpCommandFinished(int, bool error)
{
    if (error)
    {
        cleanupFile();
        m_ftp->close();

        if (m_transferState != NoTransfer) // may be a user abort operation
        {
            updateState(Error);
            updateError(FtpError, m_ftp->errorString());
        }

        return;
    }

    switch (m_ftp->currentCommand())
    {
    case QFtp::Get:
        cleanupFile();
        emit downloadFinished();
        updateState(NoTransfer);
        return;
    case QFtp::List:
        emit refreshFinished();
        updateState(NoTransfer);
        return;
    case QFtp::Put:
        cleanupFile();
        emit uploadFinished();
        break;
    case QFtp::Remove:
        emit removeFinished();
        break;
    case QFtp::Rmdir:
        emit removeDirectoryFinished();
        break;
    case QFtp::Mkdir:
        emit createDirectoryFinished();
        break;
    default:
        return;
    }

    updateState(NoTransfer);
    refreshFiles();
}
} // namespace qtquickvcp
<commit_msg>ApplicationFile: update state earlier and don't refresh after upload<commit_after>/****************************************************************************
**
** Copyright (C) 2014 Alexander Rössler
** License: LGPL version 2.1
**
** This file is part of QtQuickVcp.
**
** All rights reserved. This program and the accompanying materials
** are made available under the terms of the GNU Lesser General Public License
** (LGPL) version 2.1 which accompanies this distribution, and is available at
** http://www.gnu.org/licenses/lgpl-2.1.html
**
** This library is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
** Lesser General Public License for more details.
**
** Contributors:
** Alexander Rössler @ The Cool Tool GmbH <mail DOT aroessler AT gmail DOT com>
**
****************************************************************************/

#include "applicationfile.h"
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QCoreApplication>
#include <QFileInfo>
#include <QDir>

namespace qtquickvcp {

/*!
    \qmltype ApplicationFile
    \instantiates ApplicationFile
    \inqmlmodule Machinekit.Application
    \brief A remote file handler.
    \ingroup application

    This component interfaces with the Machinekit file service.
*/

/*! \property string ApplicationFile::localFilePath

    This property holds the path of the local file when loaded.
*/

/*! \property string ApplicationFile::localPath

    This property holds the path of the local directory for storing files.
*/

/*! \property string ApplicationFile::remoteFilePath

    This property holds the path of the remote file.

    The local file name is extracted from this property.
*/

/*! \property string ApplicationFile::remotePath

    This property holds the path of the remote directory for storing files.
*/

ApplicationFile::ApplicationFile(QObject *parent) :
    QObject(parent),
    m_uri(QLatin1String("")),
    m_localFilePath(QStringLiteral("file://")),
    m_remoteFilePath(QStringLiteral("file://")),
    m_localPath(QLatin1String("")),
    m_remotePath(QLatin1String("")),
    m_serverDirectory(QLatin1String("")),
    m_transferState(NoTransfer),
    m_error(NoError),
    m_errorString(QLatin1String("")),
    m_progress(0.0),
    m_networkReady(false),
    m_model(nullptr),
    m_ready(false),
    m_showHidden(false),
    m_networkManager(nullptr),
    m_file(nullptr),
    m_ftp(nullptr)
{
    m_temporaryDir = std::make_unique<QTemporaryDir>();
    m_temporaryDir->setAutoRemove(true);
    m_localPath = QUrl::fromLocalFile(m_temporaryDir->path());

    m_model = new ApplicationFileModel(this);

    m_networkManager = new QNetworkAccessManager(this);
    connect(m_networkManager, &QNetworkAccessManager::networkAccessibleChanged,
            this, &ApplicationFile::networkAccessibleChanged);

    if (m_networkManager->networkAccessible() == QNetworkAccessManager::Accessible)
    {
        initializeFtp();
    }
}

ApplicationFile::~ApplicationFile()
{
    cleanupFtp();
    cleanupFile();
    m_networkManager->deleteLater();
    m_model->deleteLater();
}

void ApplicationFile::startUpload()
{
    QUrl url;
    QFileInfo fileInfo(m_localFilePath.toLocalFile());
    QString remotePath;

    if (!ready() || (m_transferState != NoTransfer))
    {
        return;
    }

    url.setUrl(m_uri);
    remotePath = m_remotePath.toLocalFile();
    if (!m_serverDirectory.isEmpty())
    {
        remotePath.append("/");
        remotePath.append(m_serverDirectory);
    }
    m_remoteFilePath = QUrl::fromLocalFile(QDir(remotePath).filePath(fileInfo.fileName())).toString();
    emit remoteFilePathChanged(m_remoteFilePath.toString());

    m_file = std::make_unique<QFile>(fileInfo.filePath(), this);

    if (m_file->open(QIODevice::ReadOnly))
    {
        m_ftp->connectToHost(url.host(), static_cast<quint16>(url.port()));
        m_ftp->login();
        if (!m_serverDirectory.isEmpty())
        {
            m_ftp->cd(m_serverDirectory);
        }
        m_ftp->put(m_file.get(), fileInfo.fileName(), QFtp::Binary);
        m_ftp->close();

        m_progress = 0.0;
        emit progressChanged(m_progress);
        updateState(UploadRunning);
        updateError(NoError, QLatin1String(""));
    }
    else
    {
        updateState(Error);
        updateError(FileError, m_file->errorString());
    }
}

void ApplicationFile::startDownload()
{
    QUrl url;
    QDir dir;
    QString localFilePath;
    QString remoteFilePath;
    QString remotePath;
    QString fileName;

    if (!ready() || (m_transferState != NoTransfer))
    {
        qDebug() << "download failed" << ready() << m_transferState;
        return;
    }

    remoteFilePath = QDir::cleanPath(m_remoteFilePath.toLocalFile());
    remotePath = QDir::cleanPath(m_remotePath.toLocalFile());
    fileName = remoteFilePath.mid(remotePath.length() + 1);

    int i = fileName.indexOf(QLatin1String("/"));
    if (i > -1) // not in root directory
    {
        m_serverDirectory = fileName.left(i);
        emit serverDirectoryChanged(m_serverDirectory);
        fileName = fileName.mid(i + 1);
    }

    url.setUrl(m_uri);

    localFilePath = applicationFilePath(fileName, m_serverDirectory);
    m_localFilePath = QUrl::fromLocalFile(localFilePath);
    emit localFilePathChanged(m_localFilePath.toString());

    QFileInfo fileInfo(localFilePath);

    if (!dir.mkpath(fileInfo.absolutePath()))
    {
        updateState(Error);
        updateError(FileError, tr("Cannot create directory: %1").arg(fileInfo.absolutePath()));
        return;
    }

    m_file = std::make_unique<QFile>(localFilePath, this);

    if (m_file->open(QIODevice::WriteOnly))
    {
        m_ftp->connectToHost(url.host(), static_cast<quint16>(url.port()));
        m_ftp->login();
        if (!m_serverDirectory.isEmpty())
        {
            m_ftp->cd(m_serverDirectory);
        }
        m_ftp->get(fileName, m_file.get(), QFtp::Binary);
        m_ftp->close();
        m_progress = 0.0;
        emit progressChanged(m_progress);
        updateState(DownloadRunning);
        updateError(NoError, QLatin1String(""));
    }
    else
    {
        updateState(Error);
        updateError(FileError, m_file->errorString());
    }
}

void ApplicationFile::refreshFiles()
{
    QUrl url(m_uri);

    if (!ready() || (m_transferState != NoTransfer))
    {
        return;
    }

    m_model->clear();
    m_ftp->connectToHost(url.host(), static_cast<quint16>(url.port()));
    m_ftp->login();
    if (!m_serverDirectory.isEmpty())
    {
        m_ftp->cd(m_serverDirectory);
    }
    m_ftp->list();
    m_ftp->close();
    updateState(RefreshRunning);
}

void ApplicationFile::removeFile(const QString &name)
{
    QUrl url(m_uri);

    if (!ready() || (m_transferState != NoTransfer))
    {
        return;
    }

    m_ftp->connectToHost(url.host(), static_cast<quint16>(url.port()));
    m_ftp->login();
    if (!m_serverDirectory.isEmpty())
    {
        m_ftp->cd(m_serverDirectory);
    }
    m_ftp->remove(name);
    m_ftp->close();
    updateState(RemoveRunning);
}

void ApplicationFile::removeDirectory(const QString &name)
{
    QUrl url(m_uri);

    if (!ready() || (m_transferState != NoTransfer))
    {
        return;
    }

    m_ftp->connectToHost(url.host(), static_cast<quint16>(url.port()));
    m_ftp->login();
    if (!m_serverDirectory.isEmpty())
    {
        m_ftp->cd(m_serverDirectory);
    }
    m_ftp->rmdir(name);
    m_ftp->close();
    updateState(RemoveDirectoryRunning);
}

void ApplicationFile::createDirectory(const QString &name)
{
    QUrl url(m_uri);

    if (!ready() || (m_transferState != NoTransfer))
    {
        return;
    }

    m_ftp->connectToHost(url.host(), static_cast<quint16>(url.port()));
    m_ftp->login();
    if (!m_serverDirectory.isEmpty())
    {
        m_ftp->cd(m_serverDirectory);
    }
    m_ftp->mkdir(name);
    m_ftp->close();
    updateState(CreateDirectoryRunning);
}

void ApplicationFile::abort()
{
    if (!m_ftp)
    {
        return;
    }
    updateState(NoTransfer);
    m_ftp->abort();
    m_ftp->close();
}

void ApplicationFile::clearError()
{
    updateState(NoTransfer);
    updateError(NoError, QLatin1String(""));
}

void ApplicationFile::updateState(ApplicationFile::TransferState state)
{
    if (state != m_transferState)
    {
        m_transferState = state;
        emit transferStateChanged(m_transferState);
    }
}

void ApplicationFile::updateError(ApplicationFile::TransferError error, const QString &errorString)
{
    if (m_errorString != errorString)
    {
        m_errorString = errorString;
        emit errorStringChanged(m_errorString);
    }

    if (m_error != error)
    {
        m_error = error;
        emit errorChanged(m_error);
    }
}

QString ApplicationFile::applicationFilePath(const QString &fileName, const QString &serverDirectory) const
{
    const auto serverPath = QDir(m_localPath.toLocalFile()).filePath(serverDirectory);
    return QDir(serverPath).absoluteFilePath(fileName);
}

void ApplicationFile::initializeFtp()
{
    m_ftp = new QFtp(this);
    connect(m_ftp, &QFtp::commandFinished,
    this, &ApplicationFile::ftpCommandFinished);
    connect(m_ftp, &QFtp::listInfo,
    this, &ApplicationFile::addToList);
    connect(m_ftp, &QFtp::dataTransferProgress,
    this, &ApplicationFile::transferProgress);

    m_networkReady = true;
    emit readyChanged(m_networkReady);
}

void ApplicationFile::cleanupFtp()
{
    if (m_ftp == nullptr)
    {
        return;
    }

    m_ftp->abort();
    m_ftp->deleteLater();
    m_ftp = nullptr;

    m_networkReady = false;
    emit readyChanged(m_networkReady);
}

void ApplicationFile::cleanupFile()
{
    if (!m_file)
    {
        return;
    }

    m_file->close();
    m_file.release();
}

void ApplicationFile::transferProgress(qint64 bytesSent, qint64 bytesTotal)
{
    m_progress = static_cast<double>(bytesSent) / static_cast<double>(bytesTotal);
    emit progressChanged(m_progress);
}

void ApplicationFile::networkAccessibleChanged(QNetworkAccessManager::NetworkAccessibility accesible)
{
    if (accesible == QNetworkAccessManager::Accessible)
    {
        initializeFtp();
    }
    else
    {
        cleanupFtp();
    }
}

void ApplicationFile::addToList(const QUrlInfo &urlInfo)
{
    if (!m_showHidden && urlInfo.name().startsWith(QLatin1String("."))) {
        return;
    }

    auto item = new ApplicationFileItem();
    item->setName(urlInfo.name());
    item->setSize(urlInfo.size());
    item->setOwner(urlInfo.owner());
    item->setGroup(urlInfo.group());
    item->setLastModified(urlInfo.lastModified());
    item->setDir(urlInfo.isDir());

    m_model->addItem(item);
}

void ApplicationFile::ftpCommandFinished(int, bool error)
{
    if (error)
    {
        cleanupFile();
        m_ftp->close();

        if (m_transferState != NoTransfer) // may be a user abort operation
        {
            updateState(Error);
            updateError(FtpError, m_ftp->errorString());
        }

        return;
    }

    switch (m_ftp->currentCommand())
    {
    case QFtp::Get:
        cleanupFile();
        updateState(NoTransfer);
        emit downloadFinished();
        return;
    case QFtp::List:
        updateState(NoTransfer);
        emit refreshFinished();
        return;
    case QFtp::Put:
        cleanupFile();
        updateState(NoTransfer);
        emit uploadFinished();
        return;
    case QFtp::Remove:
        updateState(NoTransfer);
        emit removeFinished();
        break;
    case QFtp::Rmdir:
        updateState(NoTransfer);
        emit removeDirectoryFinished();
        break;
    case QFtp::Mkdir:
        updateState(NoTransfer);
        emit createDirectoryFinished();
        break;
    default:
        return;
    }

    refreshFiles();
}
} // namespace qtquickvcp
<|endoftext|>


<commit_before>#include <boost/foreach.hpp>
#include <boost/program_options.hpp>
namespace po = boost::program_options;

#include <vw/FileIO.h>
#include <vw/Image.h>
#include <vw/Cartography.h>
using namespace vw;

struct Options {
  Options() : nodata(-1), threshold(-1) {}
  // Input
  std::vector<std::string> input_files;
  double nodata, threshold;
};

// Operational Code
template <class PixelT>
void shadow_mask_nodata( Options& opt,
                         std::string input,
                         std::string output ) {
  VW_ASSERT( opt.nodata != -1, ArgumentErr() << "Missing user supplied nodata value" );
  typedef typename PixelChannelType<PixelT>::type ChannelT;
  typedef typename MaskedPixelType<PixelT>::type PMaskT;
  if ( opt.threshold < 0 ) {
    ChannelRange<ChannelT> helper;
    opt.threshold = 0.1*helper.max();
  }

  cartography::GeoReference georef;
  cartography::read_georeference(georef, input);
  DiskImageView<PixelT> input_image(input);
  ImageViewRef<PMaskT> masked_input = create_mask(input_image,opt.nodata);
  ImageViewRef<PMaskT> result =
    intersect_mask(masked_input,create_mask(threshold(input_image,opt.threshold)));
  cartography::write_georeferenced_image(output, create_mask(result,opt.nodata),
                                         georef, TerminalProgressCallback("photometrytk","Writing:"));
}

template <class PixelT>
class ThresholdAlphaFunctor : public UnaryReturnSameType {
  typedef typename vw::PixelChannelType<PixelT>::type channel_type;
  double m_threshold;
public:
  ThresholdAlphaFunctor( double t ) : m_threshold(t) {}

  template <class ArgT>
  inline ArgT operator()( ArgT const& arg ) const {
    ArgT copy = arg;
    if ( PixelGray<channel_type>(copy) <= m_threshold ) {
      copy.a() = 0;
    }
    return copy;
  }
};

template <class PixelT>
void shadow_mask_alpha( Options& opt,
                        std::string input,
                        std::string output ) {
  typedef typename PixelChannelType<PixelT>::type ChannelT;
  if ( opt.threshold < 0 ) {
    ChannelRange<ChannelT> helper;
    opt.threshold = 0.1*helper.max();
  }

  cartography::GeoReference georef;
  cartography::read_georeference(georef, input);
  DiskImageView<PixelT> input_image(input);
  ImageViewRef<PixelT> result =
    UnaryPerPixelView<DiskImageView<PixelT>,ThresholdAlphaFunctor<PixelT> >(input_image,ThresholdAlphaFunctor<PixelT>(opt.threshold));
  cartography::write_georeferenced_image(output, result, georef,
                                         TerminalProgressCallback("photometrytk","Writing:"));
}

// Code for handing input
void handle_arguments( int argc, char *argv[], Options& opt ) {
  po::options_description general_options("");
  general_options.add_options()
    ("nodata-value", po::value<double>(&opt.nodata), "Value that is nodata in the input image. Not used if input has alpha.")
    ("threshold,t", po::value<double>(&opt.threshold), "Value used to detect shadows.")
    ("help,h", "Display this help message");

  po::options_description positional("");
  positional.add_options()
    ("input-files", po::value<std::vector<std::string> >(&opt.input_files));

  po::positional_options_description positional_desc;
  positional_desc.add("input-files", -1);

  po::options_description all_options;
  all_options.add(general_options).add(positional);

  po::variables_map vm;
  try {
    po::store( po::command_line_parser( argc, argv ).options(all_options).positional(positional_desc).run(), vm );
    po::notify( vm );
  } catch (po::error &e) {
    vw_throw( ArgumentErr() << "Error parsing input:\n\t"
              << e.what() << general_options );
  }

  std::ostringstream usage;
  usage << "Usage: " << argv[0] << " [options] <image-files>\n";

  if ( vm.count("help") )
    vw_throw( ArgumentErr() << usage.str() << general_options );
  if ( opt.input_files.empty() )
    vw_throw( ArgumentErr() << "Missing input files!\n"
              << usage.str() << general_options );
}

int main( int argc, char *argv[] ) {

  Options opt;
  try {
    handle_arguments( argc, argv, opt );

    BOOST_FOREACH( const std::string& input, opt.input_files ) {

      // Determining the format of the input
      DiskImageResource *rsrc = DiskImageResource::open(input);
      ChannelTypeEnum channel_type = rsrc->channel_type();
      PixelFormatEnum pixel_format = rsrc->pixel_format();
      delete rsrc;

      vw_out() << "Loading: " << input << "\n";
      size_t pt_idx = input.rfind(".");
      std::string output = input.substr(0,pt_idx)+"_shdw" +
        input.substr(pt_idx,input.size()-pt_idx);

      switch (pixel_format) {
      case VW_PIXEL_GRAY:
        switch (channel_type) {
        case VW_CHANNEL_UINT8:
          shadow_mask_nodata<PixelGray<uint8> >( opt, input, output ); break;
        case VW_CHANNEL_INT16:
          shadow_mask_nodata<PixelGray<int16> >( opt, input, output ); break;
        case VW_CHANNEL_UINT16:
          shadow_mask_nodata<PixelGray<uint16> >( opt, input, output ); break;
        default:
          shadow_mask_nodata<PixelGray<float32> >( opt, input, output ); break;
        }
        break;
      case VW_PIXEL_GRAYA:
        switch (channel_type) {
        case VW_CHANNEL_UINT8:
          shadow_mask_alpha<PixelGrayA<uint8> >( opt, input, output ); break;
        case VW_CHANNEL_INT16:
          shadow_mask_alpha<PixelGrayA<int16> >( opt, input, output ); break;
        case VW_CHANNEL_UINT16:
          shadow_mask_alpha<PixelGrayA<uint16> >( opt, input, output ); break;
        default:
          shadow_mask_alpha<PixelGrayA<float32> >( opt, input, output ); break;
        }
        break;
      case VW_PIXEL_RGB:
        switch (channel_type) {
        case VW_CHANNEL_UINT8:
          shadow_mask_nodata<PixelRGB<uint8> >( opt, input, output ); break;
        case VW_CHANNEL_INT16:
          shadow_mask_nodata<PixelRGB<int16> >( opt, input, output ); break;
        case VW_CHANNEL_UINT16:
          shadow_mask_nodata<PixelRGB<uint16> >( opt, input, output ); break;
        default:
          shadow_mask_nodata<PixelRGB<float32> >( opt, input, output ); break;
        }
        break;
      default:
        switch (channel_type) {
        case VW_CHANNEL_UINT8:
          shadow_mask_alpha<PixelRGBA<uint8> >( opt, input, output ); break;
        case VW_CHANNEL_INT16:
          shadow_mask_alpha<PixelRGBA<int16> >( opt, input, output ); break;
        case VW_CHANNEL_UINT16:
          shadow_mask_alpha<PixelRGBA<uint16> >( opt, input, output ); break;
        default:
          shadow_mask_alpha<PixelRGBA<float32> >( opt, input, output ); break;
        }
        break;
      }

    }

  } catch ( const ArgumentErr& e ) {
    vw_out() << e.what() << std::endl;
    return 1;
  } catch ( const Exception& e ) {
    std::cerr << "Error: " << e.what() << std::endl;
    return 1;
  }

  return 0;
}
<commit_msg>Added a feather option to shadow mask<commit_after>#include <boost/foreach.hpp>
#include <boost/program_options.hpp>
namespace po = boost::program_options;

#include <vw/FileIO.h>
#include <vw/Image.h>
#include <vw/Cartography.h>
using namespace vw;

struct Options {
  Options() : nodata(-1), threshold(-1) {}
  // Input
  std::vector<std::string> input_files;
  double nodata, threshold;
  bool feather;
};

// Operational Code
template <class PixelT>
void shadow_mask_nodata( Options& opt,
                         std::string input,
                         std::string output ) {
  VW_ASSERT( opt.nodata != -1, ArgumentErr() << "Missing user supplied nodata value" );
  typedef typename PixelChannelType<PixelT>::type ChannelT;
  typedef typename MaskedPixelType<PixelT>::type PMaskT;
  if ( opt.threshold < 0 ) {
    ChannelRange<ChannelT> helper;
    opt.threshold = 0.1*helper.max();
  }

  cartography::GeoReference georef;
  cartography::read_georeference(georef, input);
  DiskImageView<PixelT> input_image(input);
  ImageViewRef<PMaskT> masked_input = create_mask(input_image,opt.nodata);
  ImageViewRef<PMaskT> result =
    intersect_mask(masked_input,create_mask(threshold(input_image,opt.threshold)));
  cartography::write_georeferenced_image(output, create_mask(result,opt.nodata),
                                         georef, TerminalProgressCallback("photometrytk","Writing:"));

}

template <class PixelT>
class ThresholdAlphaFunctor : public UnaryReturnSameType {
  typedef typename vw::PixelChannelType<PixelT>::type channel_type;
  double m_threshold;
public:
  ThresholdAlphaFunctor( double t ) : m_threshold(t) {}

  template <class ArgT>
  inline ArgT operator()( ArgT const& arg ) const {
    ArgT copy = arg;
    if ( PixelGray<channel_type>(copy) <= m_threshold ) {
      copy.a() = 0;
    }
    return copy;
  }
};

template <class PixelT>
struct MultiplyAlphaFunctor : public vw::ReturnFixedType<PixelT> {
  template <class Pixel2T>
  inline PixelT operator()( PixelT const& arg1, Pixel2T const& arg2 ) const {
    PixelT copy = arg1;
    copy.a() = float(copy.a()) * float(arg2);
    if ( copy.a() == 0 && arg1.a() != 0 && arg2 != 0 )
      copy.a() = arg1.a();
    return copy;
  }
};

template <class Image1T, class Image2T>
BinaryPerPixelView<Image1T,Image2T,MultiplyAlphaFunctor<typename Image1T::pixel_type> >
inline multiply_alpha( ImageViewBase<Image1T> const& image,
                       ImageViewBase<Image2T> const& multiply ) {
  typedef MultiplyAlphaFunctor<typename Image1T::pixel_type> FuncT;
  return BinaryPerPixelView<Image1T,Image2T,FuncT>( image.impl(), multiply.impl(), FuncT() );
}

template <class PixelT>
void shadow_mask_alpha( Options& opt,
                        std::string input,
                        std::string output ) {
  typedef typename PixelChannelType<PixelT>::type ChannelT;
  typedef typename PixelWithoutAlpha<PixelT>::type PixelNoAT;
  ChannelRange<ChannelT> helper;
  if ( opt.threshold < 0 ) {
    opt.threshold = 0.1*helper.max();
  }

  cartography::GeoReference georef;
  cartography::read_georeference(georef, input);
  DiskImageView<PixelT> input_image(input);
  if ( opt.feather ) {
    ImageViewRef<PixelNoAT> invert_holes =
      threshold(apply_mask(alpha_to_mask(input_image),helper.max()),
                opt.threshold,0,helper.max());
    ImageView<int32> dist = grassfire(invert_holes);
    ImageViewRef<double> alpha_mod = clamp(channel_cast<double>(dist)/15.0,1.0);
    ImageViewRef<PixelT> result = multiply_alpha( input_image, alpha_mod );

    cartography::write_georeferenced_image(output, result, georef,
                                           TerminalProgressCallback("photometrytk","Writing:"));
  } else {
    ImageViewRef<PixelT> result =
      UnaryPerPixelView<DiskImageView<PixelT>,ThresholdAlphaFunctor<PixelT> >(input_image,ThresholdAlphaFunctor<PixelT>(opt.threshold));

    cartography::write_georeferenced_image(output, result, georef,
                                           TerminalProgressCallback("photometrytk","Writing:"));
  }
}

// Code for handing input
void handle_arguments( int argc, char *argv[], Options& opt ) {
  po::options_description general_options("");
  general_options.add_options()
    ("nodata-value", po::value<double>(&opt.nodata), "Value that is nodata in the input image. Not used if input has alpha.")
    ("threshold,t", po::value<double>(&opt.threshold), "Value used to detect shadows.")
    ("feather", "Feather pre-existing alpha around the newly masked shadows. Only for images with alpha already.")
    ("help,h", "Display this help message");

  po::options_description positional("");
  positional.add_options()
    ("input-files", po::value<std::vector<std::string> >(&opt.input_files));

  po::positional_options_description positional_desc;
  positional_desc.add("input-files", -1);

  po::options_description all_options;
  all_options.add(general_options).add(positional);

  po::variables_map vm;
  try {
    po::store( po::command_line_parser( argc, argv ).options(all_options).positional(positional_desc).run(), vm );
    po::notify( vm );
  } catch (po::error &e) {
    vw_throw( ArgumentErr() << "Error parsing input:\n\t"
              << e.what() << general_options );
  }

  std::ostringstream usage;
  usage << "Usage: " << argv[0] << " [options] <image-files>\n";
  opt.feather = vm.count("feather");

  if ( vm.count("help") )
    vw_throw( ArgumentErr() << usage.str() << general_options );
  if ( opt.input_files.empty() )
    vw_throw( ArgumentErr() << "Missing input files!\n"
              << usage.str() << general_options );
}

int main( int argc, char *argv[] ) {

  Options opt;
  try {
    handle_arguments( argc, argv, opt );

    BOOST_FOREACH( const std::string& input, opt.input_files ) {

      // Determining the format of the input
      DiskImageResource *rsrc = DiskImageResource::open(input);
      ChannelTypeEnum channel_type = rsrc->channel_type();
      PixelFormatEnum pixel_format = rsrc->pixel_format();
      delete rsrc;

      vw_out() << "Loading: " << input << "\n";
      size_t pt_idx = input.rfind(".");
      std::string output = input.substr(0,pt_idx)+"_shdw" +
        input.substr(pt_idx,input.size()-pt_idx);

      switch (pixel_format) {
      case VW_PIXEL_GRAY:
        switch (channel_type) {
        case VW_CHANNEL_UINT8:
          shadow_mask_nodata<PixelGray<uint8> >( opt, input, output ); break;
        case VW_CHANNEL_INT16:
          shadow_mask_nodata<PixelGray<int16> >( opt, input, output ); break;
        case VW_CHANNEL_UINT16:
          shadow_mask_nodata<PixelGray<uint16> >( opt, input, output ); break;
        default:
          shadow_mask_nodata<PixelGray<float32> >( opt, input, output ); break;
        }
        break;
      case VW_PIXEL_GRAYA:
        switch (channel_type) {
        case VW_CHANNEL_UINT8:
          shadow_mask_alpha<PixelGrayA<uint8> >( opt, input, output ); break;
        case VW_CHANNEL_INT16:
          shadow_mask_alpha<PixelGrayA<int16> >( opt, input, output ); break;
        case VW_CHANNEL_UINT16:
          shadow_mask_alpha<PixelGrayA<uint16> >( opt, input, output ); break;
        default:
          shadow_mask_alpha<PixelGrayA<float32> >( opt, input, output ); break;
        }
        break;
      case VW_PIXEL_RGB:
        switch (channel_type) {
        case VW_CHANNEL_UINT8:
          shadow_mask_nodata<PixelRGB<uint8> >( opt, input, output ); break;
        case VW_CHANNEL_INT16:
          shadow_mask_nodata<PixelRGB<int16> >( opt, input, output ); break;
        case VW_CHANNEL_UINT16:
          shadow_mask_nodata<PixelRGB<uint16> >( opt, input, output ); break;
        default:
          shadow_mask_nodata<PixelRGB<float32> >( opt, input, output ); break;
        }
        break;
      default:
        switch (channel_type) {
        case VW_CHANNEL_UINT8:
          shadow_mask_alpha<PixelRGBA<uint8> >( opt, input, output ); break;
        case VW_CHANNEL_INT16:
          shadow_mask_alpha<PixelRGBA<int16> >( opt, input, output ); break;
        case VW_CHANNEL_UINT16:
          shadow_mask_alpha<PixelRGBA<uint16> >( opt, input, output ); break;
        default:
          shadow_mask_alpha<PixelRGBA<float32> >( opt, input, output ); break;
        }
        break;
      }

    }

  } catch ( const ArgumentErr& e ) {
    vw_out() << e.what() << std::endl;
    return 1;
  } catch ( const Exception& e ) {
    std::cerr << "Error: " << e.what() << std::endl;
    return 1;
  }

  return 0;
}
<|endoftext|>


<commit_before>//..............................................................................
//
//  This file is part of the AXL library.
//
//  AXL is distributed under the MIT license.
//  For details see accompanying license.txt file,
//  the public copy of which is also available at:
//  http://tibbo.com/downloads/archive/axl/license.txt
//
//..............................................................................

#include "pch.h"
#include "axl_io_FilePathUtils.h"
#include "axl_err_Error.h"
#include "axl_enc_Utf.h"

#if (_AXL_OS_WIN)
#	include "axl_io_File.h"
#	include "axl_sys_win_NtDll.h"
#	include "axl_sys_win_NtStatus.h"
#endif

//..............................................................................

#if (_AXL_OS_DARWIN)
char*
get_current_dir_name ()
{
	size_t size = 128;

	for (;;)
	{
		char* buffer = (char*) ::malloc (size);
		char* result = ::getcwd (buffer, size);
		if (result == buffer)
			return buffer;

		::free (buffer);

		if (errno != ERANGE)
			return NULL;

		size *= 2;
	}
}
#endif

namespace axl {
namespace io {

//..............................................................................

sl::String
getTempDir ()
{
#if (_AXL_OS_WIN)
	wchar_t dir [1024] = { 0 };
	::GetTempPathW (countof (dir) - 1, dir);
	return dir;
#elif (_AXL_OS_POSIX)
	return "/tmp";
#endif
}

sl::String
getCurrentDir ()
{
#if (_AXL_OS_WIN)
	wchar_t dir [1024] = { 0 };
	::GetCurrentDirectoryW (countof (dir) - 1, dir);
	return dir;
#elif (_AXL_OS_POSIX)
	char* p = ::get_current_dir_name ();
	sl::String dir = p;
	::free (p);
	return dir;
#endif
}

bool
setCurrentDir (const sl::StringRef& dir)
{
#if (_AXL_OS_WIN)
	bool_t result = ::SetCurrentDirectoryW (dir.s2 ().sz ());
	return err::complete (result);
#elif (_AXL_OS_POSIX)
	int result = ::chdir (dir.sz ());
	return err::complete (result == 0);
#endif
}

sl::String
getExeFilePath ()
{
	char buffer [1024] = { 0 };

#if (_AXL_OS_WIN)
	::GetModuleFileNameA (::GetModuleHandle (NULL), buffer, countof (buffer) - 1);
#elif (_AXL_OS_POSIX)
#	if (_AXL_OS_LINUX)
	::readlink ("/proc/self/exe", buffer, countof (buffer) - 1);
#	elif (_AXL_OS_DARWIN)
	uint32_t size = sizeof (buffer);
	_NSGetExecutablePath (buffer, &size);
#	else
#		error unsupported POSIX flavor
#	endif
#endif

	return buffer;
}

sl::String
getExeDir ()
{
	return io::getDir (io::getExeFilePath ());
}

bool
doesFileExist (const sl::StringRef& fileName)
{
#if (_AXL_OS_WIN)
	char buffer [256];
	sl::String_w fileName_w (ref::BufKind_Stack, buffer, sizeof (buffer));
	fileName_w = fileName;
	dword_t attributes = ::GetFileAttributesW (fileName_w);
	return attributes != INVALID_FILE_ATTRIBUTES;
#elif (_AXL_OS_POSIX)
	return ::access (fileName.sz (), F_OK) != -1;
#endif
}

#if (_AXL_OS_WIN)
bool
isDir (const sl::StringRef_w& fileName)
{
	dword_t attributes = ::GetFileAttributesW (fileName.sz ());
	return attributes != INVALID_FILE_ATTRIBUTES && (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
}

bool
isDir (const sl::StringRef& fileName)
{
	char buffer [256];
	sl::String_w fileName_w (ref::BufKind_Stack, buffer, sizeof (buffer));
	return isDir (fileName_w);
}

bool
ensureDirExists (const sl::StringRef& fileName)
{
	char buffer [256] = { 0 };
	sl::String_w fileName_w (ref::BufKind_Stack, buffer, sizeof (buffer));
	fileName_w = fileName;

	if (fileName_w.isEmpty () || isDir (fileName_w))
		return true;

	wchar_t* p = fileName_w.getBuffer ();
	if (p [1] == ':')
		p += 2;

	while (*p == '\\' || *p == '/') // skip root
		p++;

	while (*p)
	{
		wchar_t* p2 = p + 1;
		while (*p2 && *p2 != '\\' && *p2 != '/')
			p2++;

		wchar_t c = *p2; // save
		*(wchar_t*) p2 = 0;

		if (!isDir (fileName_w))
		{
			bool_t result = ::CreateDirectoryW (fileName_w, NULL);
			if (!result)
				return err::failWithLastSystemError ();
		}

		*p2 = c; // restore

		p = p2 + 1;
		while (*p == '\\' || *p == '/') // skip separators
			p++;
	}

	return true;
}

#elif (_AXL_OS_POSIX)

bool
isDir (const sl::StringRef& fileName)
{
	struct stat st;

	int result = ::stat (fileName.sz (), &st);
	return result == 0 && S_ISDIR (st.st_mode);
}

bool
ensureDirExists (const sl::StringRef& fileName0)
{
	if (fileName0.isEmpty() || isDir (fileName0))
		return true;

	sl::String fileName = fileName0;

	char* p = fileName.getBuffer (); // ensure exclusive buffer (we're going to modify it)
	while (*p == '/') // skip root
		p++;

	while (*p)
	{
		char* p2 = p + 1;
		while (*p2 && *p2 != '/')
			p2++;

		*p2 = 0;

		if (!isDir (fileName))
		{
			int result = ::mkdir (fileName, S_IRWXU);
			if (result != 0)
				return err::failWithLastSystemError ();
		}

		*p2 = '/'; // restore

		p = p2 + 1;
		while (*p == '/') // skip separators
			p++;
	}

	return true;
}

#endif

sl::String
getFullFilePath (const sl::StringRef& fileName)
{
#if (_AXL_OS_WIN)
	char buffer [256];
	sl::String_w fileName_w (ref::BufKind_Stack, buffer, sizeof (buffer));
	fileName_w = fileName;

	size_t bufferLength = ::GetFullPathNameW (fileName_w, 0, NULL, NULL);
	if (!bufferLength)
		return err::failWithLastSystemError (NULL);

	sl::String_w filePath;

	for (;;)
	{
		wchar_t* p = filePath.createBuffer (bufferLength);
		if (!p)
			return NULL;

		size_t actualLength = ::GetFullPathNameW (fileName_w, bufferLength, p, NULL);
		ASSERT (actualLength != bufferLength); // on success, it should be bufferLength - 1

		if (!actualLength)
			return err::failWithLastSystemError (NULL);

		if (actualLength <= bufferLength)
			return sl::String (filePath, actualLength);

		bufferLength = actualLength; // try again with a bigger buffer
	}

#elif (_AXL_OS_POSIX)
	char fullPath [PATH_MAX] = { 0 };
	::realpath (fileName.sz (), fullPath);
	return fullPath;
#endif
}

sl::String
getDir (const sl::StringRef& filePath)
{
#if (_AXL_OS_WIN)
	char buffer [256];
	sl::String_w filePath_w (ref::BufKind_Stack, buffer, sizeof (buffer));
	filePath_w = filePath;

	wchar_t drive [4] = { 0 };
	wchar_t dir [1024] = { 0 };

	_wsplitpath_s (
		filePath_w,
		drive, countof (drive) - 1,
		dir, countof (dir) - 1,
		NULL, 0,
		NULL, 0
		);

	sl::String string = drive;
	string.append (dir);
	return string;

#elif (_AXL_OS_POSIX)
	size_t i = filePath.reverseFind ('/');
	return i != -1 ? filePath.getLeftSubString (i + 1) : NULL;
#endif
}

sl::String
getFileName (const sl::StringRef& filePath)
{
#if (_AXL_OS_WIN)
	char buffer [256];
	sl::String_w filePath_w (ref::BufKind_Stack, buffer, sizeof (buffer));
	filePath_w = filePath;

	wchar_t fileName [1024] = { 0 };
	wchar_t extension [1024] = { 0 };

	_wsplitpath_s (
		filePath_w,
		NULL, 0,
		NULL, 0,
		fileName, countof (fileName) - 1,
		extension, countof (extension) - 1
		);

	sl::String string = fileName;
	string.append (extension);
	return string;
#elif (_AXL_OS_POSIX)
	size_t i = filePath.reverseFind ('/');
	return i != -1 ? filePath.getSubString (i + 1) : filePath;
#endif
}

sl::String
getExtension (const sl::StringRef& filePath)
{
	size_t i = filePath.find ('.');
	return i != -1 ? filePath.getSubString (i) : NULL;
}

sl::String
concatFilePath (
	sl::String* filePath,
	const sl::StringRef& fileName
	)
{
	if (filePath->isEmpty ())
	{
		*filePath = fileName;
		return *filePath;
	}

	char last = (*filePath) [filePath->getLength () - 1];

#if (_AXL_OS_WIN)
	if (last != '\\' && last != '/')
		filePath->append ('\\');
#elif (_AXL_OS_POSIX)
	if (last != '/')
		filePath->append ('/');
#endif

	filePath->append (fileName);
	return *filePath;
}

sl::String
findFilePath (
	const sl::StringRef& fileName,
	const sl::StringRef& firstDir,
	const sl::BoxList <sl::String>* dirList,
	bool doFindInCurrentDir
	)
{
	sl::String filePath;

	if (!firstDir.isEmpty ())
	{
		filePath = concatFilePath (firstDir, fileName);
		if (doesFileExist (filePath))
			return getFullFilePath (filePath);
	}

	if (doFindInCurrentDir)
		if (doesFileExist (fileName))
			return getFullFilePath (fileName);

	if (dirList)
	{
		sl::ConstBoxIterator <sl::String> dir = dirList->getHead ();
		for (; dir; dir++)
		{
			filePath.forceCopy (*dir);
			concatFilePath (&filePath, fileName);
			if (doesFileExist (filePath))
				return getFullFilePath (filePath);
		}
	}

	return sl::String ();
}

#if (_AXL_OS_WIN)

bool
getSymbolicLinkTarget (
	sl::String* targetName,
	const sl::StringRef& linkName
	)
{
	using namespace axl::sys::win;

	NTSTATUS status;

	sl::String_w linkName_w = linkName;

	UNICODE_STRING uniLinkName;
	uniLinkName.Buffer = linkName_w.p ();
	uniLinkName.Length = linkName_w.getLength () * sizeof (WCHAR);
	uniLinkName.MaximumLength = linkName_w.getHdr ()->m_bufferSize;

	OBJECT_ATTRIBUTES oa = { 0 };
	oa.Length = sizeof (oa);
	oa.RootDirectory = NULL;
	oa.ObjectName = &uniLinkName;

	io::win::File link;
	status = ntOpenSymbolicLinkObject (
		link.p (),
		GENERIC_READ,
		&oa
		);

	if (status < 0)
	{
		err::setError (sys::win::NtStatus (status));
		return false;
	}

	UNICODE_STRING uniTarget = { 0 };
	ULONG bufferSize = 0;

	status = ntQuerySymbolicLinkObject (link, &uniTarget, &bufferSize);
	if (status != STATUS_BUFFER_TOO_SMALL)
	{
		err::setError (sys::win::NtStatus (status));
		return false;
	}

	sl::String_w targetName_w;
	wchar_t* p = targetName_w.createBuffer (bufferSize / sizeof (WCHAR));

	uniTarget.Buffer = p;
	uniTarget.Length = 0;
	uniTarget.MaximumLength = (USHORT) bufferSize;

	status = ntQuerySymbolicLinkObject (link, &uniTarget, &bufferSize);
	if (status < 0)
	{
		err::setError (sys::win::NtStatus (status));
		return false;
	}

	ASSERT (uniTarget.Length <= uniTarget.MaximumLength);
	targetName_w.setReducedLength (uniTarget.Length / sizeof (WCHAR));
	*targetName = targetName_w;
	return true;
}

#elif (_AXL_OS_POSIX)

bool
getSymbolicLinkTarget (
	sl::String* targetName,
	const sl::StringRef& linkName
	)
{
	struct stat linkStat = { 0 };

	int result = ::lstat (linkName.sz (), &linkStat);
	if (result == -1)
		return err::failWithLastSystemError ();

	if (!S_ISLNK (linkStat.st_mode))
		return err::fail (EINVAL);

	size_t bufferLength = linkStat.st_size ? linkStat.st_size : PATH_MAX;

	char* p = targetName->createBuffer (bufferLength);
	if (!p)
		return false;

	result = readlink (linkName.sz (), p, bufferLength);
	if (result == -1)
		return err::failWithLastSystemError ();

	ASSERT (result <= bufferLength);
	targetName->setReducedLength (result);
	return true;
}

#endif

//..............................................................................

} // namespace io
} // namespace axl
<commit_msg>[axl_io] fix: buffer overflow in io::ensureDirExists<commit_after>//..............................................................................
//
//  This file is part of the AXL library.
//
//  AXL is distributed under the MIT license.
//  For details see accompanying license.txt file,
//  the public copy of which is also available at:
//  http://tibbo.com/downloads/archive/axl/license.txt
//
//..............................................................................

#include "pch.h"
#include "axl_io_FilePathUtils.h"
#include "axl_err_Error.h"
#include "axl_enc_Utf.h"

#if (_AXL_OS_WIN)
#	include "axl_io_File.h"
#	include "axl_sys_win_NtDll.h"
#	include "axl_sys_win_NtStatus.h"
#endif

//..............................................................................

#if (_AXL_OS_DARWIN)
char*
get_current_dir_name ()
{
	size_t size = 128;

	for (;;)
	{
		char* buffer = (char*) ::malloc (size);
		char* result = ::getcwd (buffer, size);
		if (result == buffer)
			return buffer;

		::free (buffer);

		if (errno != ERANGE)
			return NULL;

		size *= 2;
	}
}
#endif

namespace axl {
namespace io {

//..............................................................................

sl::String
getTempDir ()
{
#if (_AXL_OS_WIN)
	wchar_t dir [1024] = { 0 };
	::GetTempPathW (countof (dir) - 1, dir);
	return dir;
#elif (_AXL_OS_POSIX)
	return "/tmp";
#endif
}

sl::String
getCurrentDir ()
{
#if (_AXL_OS_WIN)
	wchar_t dir [1024] = { 0 };
	::GetCurrentDirectoryW (countof (dir) - 1, dir);
	return dir;
#elif (_AXL_OS_POSIX)
	char* p = ::get_current_dir_name ();
	sl::String dir = p;
	::free (p);
	return dir;
#endif
}

bool
setCurrentDir (const sl::StringRef& dir)
{
#if (_AXL_OS_WIN)
	bool_t result = ::SetCurrentDirectoryW (dir.s2 ().sz ());
	return err::complete (result);
#elif (_AXL_OS_POSIX)
	int result = ::chdir (dir.sz ());
	return err::complete (result == 0);
#endif
}

sl::String
getExeFilePath ()
{
	char buffer [1024] = { 0 };

#if (_AXL_OS_WIN)
	::GetModuleFileNameA (::GetModuleHandle (NULL), buffer, countof (buffer) - 1);
#elif (_AXL_OS_POSIX)
#	if (_AXL_OS_LINUX)
	::readlink ("/proc/self/exe", buffer, countof (buffer) - 1);
#	elif (_AXL_OS_DARWIN)
	uint32_t size = sizeof (buffer);
	_NSGetExecutablePath (buffer, &size);
#	else
#		error unsupported POSIX flavor
#	endif
#endif

	return buffer;
}

sl::String
getExeDir ()
{
	return io::getDir (io::getExeFilePath ());
}

bool
doesFileExist (const sl::StringRef& fileName)
{
#if (_AXL_OS_WIN)
	char buffer [256];
	sl::String_w fileName_w (ref::BufKind_Stack, buffer, sizeof (buffer));
	fileName_w = fileName;
	dword_t attributes = ::GetFileAttributesW (fileName_w);
	return attributes != INVALID_FILE_ATTRIBUTES;
#elif (_AXL_OS_POSIX)
	return ::access (fileName.sz (), F_OK) != -1;
#endif
}

#if (_AXL_OS_WIN)
bool
isDir (const sl::StringRef_w& fileName)
{
	dword_t attributes = ::GetFileAttributesW (fileName.sz ());
	return attributes != INVALID_FILE_ATTRIBUTES && (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
}

bool
isDir (const sl::StringRef& fileName)
{
	char buffer [256];
	sl::String_w fileName_w (ref::BufKind_Stack, buffer, sizeof (buffer));
	return isDir (fileName_w);
}

bool
ensureDirExists (const sl::StringRef& fileName)
{
	char buffer [256] = { 0 };
	sl::String_w fileName_w (ref::BufKind_Stack, buffer, sizeof (buffer));
	fileName_w = fileName;

	if (fileName_w.isEmpty () || isDir (fileName_w))
		return true;

	wchar_t* p = fileName_w.getBuffer ();
	if (p [1] == ':')
		p += 2;

	while (*p == '\\' || *p == '/') // skip root
		p++;

	while (*p)
	{
		wchar_t* p2 = p + 1;
		while (*p2 && *p2 != '\\' && *p2 != '/')
			p2++;

		wchar_t c = *p2; // save
		*(wchar_t*) p2 = 0;

		if (!isDir (fileName_w))
		{
			bool_t result = ::CreateDirectoryW (fileName_w, NULL);
			if (!result)
				return err::failWithLastSystemError ();
		}

		if (!c)
			break;

		*p2 = c; // restore

		p = p2 + 1;
		while (*p == '\\' || *p == '/') // skip separators
			p++;
	}

	return true;
}

#elif (_AXL_OS_POSIX)

bool
isDir (const sl::StringRef& fileName)
{
	struct stat st;

	int result = ::stat (fileName.sz (), &st);
	return result == 0 && S_ISDIR (st.st_mode);
}

bool
ensureDirExists (const sl::StringRef& fileName0)
{
	if (fileName0.isEmpty() || isDir (fileName0))
		return true;

	sl::String fileName = fileName0;

	char* p = fileName.getBuffer (); // ensure exclusive buffer (we're going to modify it)
	while (*p == '/') // skip root
		p++;

	while (*p)
	{
		char* p2 = p + 1;
		while (*p2 && *p2 != '/')
			p2++;

		char c = *p2; // save
		*p2 = 0;

		if (!isDir (fileName))
		{
			int result = ::mkdir (fileName, S_IRWXU);
			if (result != 0)
				return err::failWithLastSystemError ();
		}

		if (!c)
			break;

		*p2 = c; // restore

		p = p2 + 1;
		while (*p == '/') // skip separators
			p++;
	}

	return true;
}

#endif

sl::String
getFullFilePath (const sl::StringRef& fileName)
{
#if (_AXL_OS_WIN)
	char buffer [256];
	sl::String_w fileName_w (ref::BufKind_Stack, buffer, sizeof (buffer));
	fileName_w = fileName;

	size_t bufferLength = ::GetFullPathNameW (fileName_w, 0, NULL, NULL);
	if (!bufferLength)
		return err::failWithLastSystemError (NULL);

	sl::String_w filePath;

	for (;;)
	{
		wchar_t* p = filePath.createBuffer (bufferLength);
		if (!p)
			return NULL;

		size_t actualLength = ::GetFullPathNameW (fileName_w, bufferLength, p, NULL);
		ASSERT (actualLength != bufferLength); // on success, it should be bufferLength - 1

		if (!actualLength)
			return err::failWithLastSystemError (NULL);

		if (actualLength <= bufferLength)
			return sl::String (filePath, actualLength);

		bufferLength = actualLength; // try again with a bigger buffer
	}

#elif (_AXL_OS_POSIX)
	char fullPath [PATH_MAX] = { 0 };
	::realpath (fileName.sz (), fullPath);
	return fullPath;
#endif
}

sl::String
getDir (const sl::StringRef& filePath)
{
#if (_AXL_OS_WIN)
	char buffer [256];
	sl::String_w filePath_w (ref::BufKind_Stack, buffer, sizeof (buffer));
	filePath_w = filePath;

	wchar_t drive [4] = { 0 };
	wchar_t dir [1024] = { 0 };

	_wsplitpath_s (
		filePath_w,
		drive, countof (drive) - 1,
		dir, countof (dir) - 1,
		NULL, 0,
		NULL, 0
		);

	sl::String string = drive;
	string.append (dir);
	return string;

#elif (_AXL_OS_POSIX)
	size_t i = filePath.reverseFind ('/');
	return i != -1 ? filePath.getLeftSubString (i + 1) : NULL;
#endif
}

sl::String
getFileName (const sl::StringRef& filePath)
{
#if (_AXL_OS_WIN)
	char buffer [256];
	sl::String_w filePath_w (ref::BufKind_Stack, buffer, sizeof (buffer));
	filePath_w = filePath;

	wchar_t fileName [1024] = { 0 };
	wchar_t extension [1024] = { 0 };

	_wsplitpath_s (
		filePath_w,
		NULL, 0,
		NULL, 0,
		fileName, countof (fileName) - 1,
		extension, countof (extension) - 1
		);

	sl::String string = fileName;
	string.append (extension);
	return string;
#elif (_AXL_OS_POSIX)
	size_t i = filePath.reverseFind ('/');
	return i != -1 ? filePath.getSubString (i + 1) : filePath;
#endif
}

sl::String
getExtension (const sl::StringRef& filePath)
{
	size_t i = filePath.find ('.');
	return i != -1 ? filePath.getSubString (i) : NULL;
}

sl::String
concatFilePath (
	sl::String* filePath,
	const sl::StringRef& fileName
	)
{
	if (filePath->isEmpty ())
	{
		*filePath = fileName;
		return *filePath;
	}

	char last = (*filePath) [filePath->getLength () - 1];

#if (_AXL_OS_WIN)
	if (last != '\\' && last != '/')
		filePath->append ('\\');
#elif (_AXL_OS_POSIX)
	if (last != '/')
		filePath->append ('/');
#endif

	filePath->append (fileName);
	return *filePath;
}

sl::String
findFilePath (
	const sl::StringRef& fileName,
	const sl::StringRef& firstDir,
	const sl::BoxList <sl::String>* dirList,
	bool doFindInCurrentDir
	)
{
	sl::String filePath;

	if (!firstDir.isEmpty ())
	{
		filePath = concatFilePath (firstDir, fileName);
		if (doesFileExist (filePath))
			return getFullFilePath (filePath);
	}

	if (doFindInCurrentDir)
		if (doesFileExist (fileName))
			return getFullFilePath (fileName);

	if (dirList)
	{
		sl::ConstBoxIterator <sl::String> dir = dirList->getHead ();
		for (; dir; dir++)
		{
			filePath.forceCopy (*dir);
			concatFilePath (&filePath, fileName);
			if (doesFileExist (filePath))
				return getFullFilePath (filePath);
		}
	}

	return sl::String ();
}

#if (_AXL_OS_WIN)

bool
getSymbolicLinkTarget (
	sl::String* targetName,
	const sl::StringRef& linkName
	)
{
	using namespace axl::sys::win;

	NTSTATUS status;

	sl::String_w linkName_w = linkName;

	UNICODE_STRING uniLinkName;
	uniLinkName.Buffer = linkName_w.p ();
	uniLinkName.Length = linkName_w.getLength () * sizeof (WCHAR);
	uniLinkName.MaximumLength = linkName_w.getHdr ()->m_bufferSize;

	OBJECT_ATTRIBUTES oa = { 0 };
	oa.Length = sizeof (oa);
	oa.RootDirectory = NULL;
	oa.ObjectName = &uniLinkName;

	io::win::File link;
	status = ntOpenSymbolicLinkObject (
		link.p (),
		GENERIC_READ,
		&oa
		);

	if (status < 0)
	{
		err::setError (sys::win::NtStatus (status));
		return false;
	}

	UNICODE_STRING uniTarget = { 0 };
	ULONG bufferSize = 0;

	status = ntQuerySymbolicLinkObject (link, &uniTarget, &bufferSize);
	if (status != STATUS_BUFFER_TOO_SMALL)
	{
		err::setError (sys::win::NtStatus (status));
		return false;
	}

	sl::String_w targetName_w;
	wchar_t* p = targetName_w.createBuffer (bufferSize / sizeof (WCHAR));

	uniTarget.Buffer = p;
	uniTarget.Length = 0;
	uniTarget.MaximumLength = (USHORT) bufferSize;

	status = ntQuerySymbolicLinkObject (link, &uniTarget, &bufferSize);
	if (status < 0)
	{
		err::setError (sys::win::NtStatus (status));
		return false;
	}

	ASSERT (uniTarget.Length <= uniTarget.MaximumLength);
	targetName_w.setReducedLength (uniTarget.Length / sizeof (WCHAR));
	*targetName = targetName_w;
	return true;
}

#elif (_AXL_OS_POSIX)

bool
getSymbolicLinkTarget (
	sl::String* targetName,
	const sl::StringRef& linkName
	)
{
	struct stat linkStat = { 0 };

	int result = ::lstat (linkName.sz (), &linkStat);
	if (result == -1)
		return err::failWithLastSystemError ();

	if (!S_ISLNK (linkStat.st_mode))
		return err::fail (EINVAL);

	size_t bufferLength = linkStat.st_size ? linkStat.st_size : PATH_MAX;

	char* p = targetName->createBuffer (bufferLength);
	if (!p)
		return false;

	result = readlink (linkName.sz (), p, bufferLength);
	if (result == -1)
		return err::failWithLastSystemError ();

	ASSERT (result <= bufferLength);
	targetName->setReducedLength (result);
	return true;
}

#endif

//..............................................................................

} // namespace io
} // namespace axl
<|endoftext|>