<commit_before>/*

  This program will add histograms (see note) and Trees from a list of root files and write them
  to a target root file. The target file is newly created and must not be
  identical to one of the source files.

  Syntax:

       hadd targetfile source1 source2 ...
    or
       hadd -f targetfile source1 source2 ...
         (targetfile is overwritten if it exists)

  When the -f option is specified, one can also specify the compression
  level of the target file. By default the compression level is 1, but
  if "-f0" is specified, the target file will not be compressed.
  if "-f6" is specified, the compression level 6 will be used.

  For example assume 3 files f1, f2, f3 containing histograms hn and Trees Tn
    f1 with h1 h2 h3 T1
    f2 with h1 h4 T1 T2
    f3 with h5
   the result of
     hadd -f x.root f1.root f2.root f3.root
   will be a file x.root with h1 h2 h3 h4 h5 T1 T2
   where h1 will be the sum of the 2 histograms in f1 and f2
         T1 will be the merge of the Trees in f1 and f2

   The files may contain sub-directories.

  if the source files contains histograms and Trees, one can skip
  the Trees with
       hadd -T targetfile source1 source2 ...

  Wildcarding and indirect files are also supported
    hadd result.root  myfil*.root
   will merge all files in myfil*.root
    hadd result.root file1.root @list.txt file2. root myfil*.root
    will merge file1. root, file2. root, all files in myfil*.root
    and all files in the indirect text file list.txt ("@" as the first
    character of the file indicates an indirect file. An indirect file
    is a text file containing a list of other files, including other
    indirect files, one line per file).

  If the sources and and target compression levels are identical (default),
  the program uses the TChain::Merge function with option "fast", ie
  the merge will be done without  unzipping or unstreaming the baskets
  (i.e. direct copy of the raw byte on disk). The "fast" mode is typically
  5 times faster than the mode unzipping and unstreaming the baskets.

  NOTE1: By default histograms are added. However hadd does not support the case where
         histograms have their bit TH1::kIsAverage set.

  NOTE2: hadd returns a status code: 0 if OK, -1 otherwise

  Authors: Rene Brun, Dirk Geppert, Sven A. Schmidt, sven.schmidt@cern.ch
         : rewritten from scratch by Rene Brun (30 November 2005)
            to support files with nested directories.
           Toby Burnett implemented the possibility to use indirect files.
 */

#include "RConfig.h"
#include <string>
#include "TFile.h"
#include "THashList.h"
#include "TKey.h"
#include "TObjString.h"
#include "Riostream.h"
#include "TClass.h"
#include "TSystem.h"
#include <stdlib.h>

#include "TFileMerger.h"

//___________________________________________________________________________
int main( int argc, char **argv )
{

   if ( argc < 3 || "-h" == std::string(argv[1]) || "--help" == std::string(argv[1]) ) {
      std::cout << "Usage: " << argv[0] << " [-f[0-9]] [-k] [-T] [-O] [-n maxopenedfiles] [-v verbosity] targetfile source1 [source2 source3 ...]" << std::endl;
      std::cout << "This program will add histograms from a list of root files and write them" << std::endl;
      std::cout << "to a target root file. The target file is newly created and must not " << std::endl;
      std::cout << "exist, or if -f (\"force\") is given, must not be one of the source files." << std::endl;
      std::cout << "Supply at least two source files for this to make sense... ;-)" << std::endl;
      std::cout << "If the option -k is used, hadd will not exit on corrupt or non-existant input files but skip the offending files instead." << std::endl;
      std::cout << "If the option -T is used, Trees are not merged" <<std::endl;
      std::cout << "If the option -O is used, when merging TTree, the basket size is re-optimized" <<std::endl;
      std::cout << "If the option -v is used, explicitly set the verbosity level; 0 request no output, 99 is the default" <<std::endl;
      std::cout << "If the option -n is used, hadd will open at most 'maxopenedfiles' at once, use 0 to request to use the system maximum." << std::endl;
      std::cout << "When -the -f option is specified, one can also specify the compression" <<std::endl;
      std::cout << "level of the target file. By default the compression level is 1, but" <<std::endl;
      std::cout << "if \"-f0\" is specified, the target file will not be compressed." <<std::endl;
      std::cout << "if \"-f6\" is specified, the compression level 6 will be used." <<std::endl;
      std::cout << "if Target and source files have different compression levels"<<std::endl;
      std::cout << " a slower method is used"<<std::endl;
      return 1;
   }

   Bool_t force = kFALSE;
   Bool_t skip_errors = kFALSE;
   Bool_t reoptimize = kFALSE;
   Bool_t noTrees = kFALSE;
   Int_t maxopenedfiles = 0;
   Int_t verbosity = 99;

   int outputPlace = 0;
   int ffirst = 2;
   Int_t newcomp = 1;
   for( int a = 1; a < argc; ++a ) {
      if ( strcmp(argv[a],"-T") == 0 ) {
         noTrees = kTRUE;
         ++ffirst;
      } else if ( strcmp(argv[a],"-f") == 0 ) {
         force = kTRUE;
         ++ffirst;
      } else if ( strcmp(argv[a],"-k") == 0 ) {
         skip_errors = kTRUE;
         ++ffirst;
      } else if ( strcmp(argv[a],"-O") == 0 ) {
         reoptimize = kTRUE;
         ++ffirst;
      } else if ( strcmp(argv[a],"-n") == 0 ) {
         if (a+1 >= argc) {
            std::cerr << "Error: no maximum number of opened was provided after -n.\n";
         } else {
            Long_t request = strtol(argv[a+1], 0, 10);
            if (request < kMaxLong && request >= 0) {
               maxopenedfiles = (Int_t)request;
               ++a;
               ++ffirst;
            } else {
               std::cerr << "Error: could not parse the max number of opened file passed after -n: " << argv[a+1] << ". We will use the system maximum.\n";
            }
         }
         ++ffirst;
      } else if ( strcmp(argv[a],"-v") == 0 ) {
         if (a+1 >= argc) {
            std::cerr << "Error: no verbosity level was provided after -v.\n";
         } else {
            Long_t request = strtol(argv[a+1], 0, 10);
            if (request < kMaxLong && request >= 0) {
               verbosity = (Int_t)request;
               ++a;
               ++ffirst;
            } else {
               std::cerr << "Error: could not parse the verbosity level passed after -v: " << argv[a+1] << ". We will use the default value (99).\n";
            }
         }
         ++ffirst;
      } else if ( argv[a][0] == '-' ) {
         char ft[4];
         for( int j=0; j<=9; ++j ) {
            snprintf(ft,4,"-f%d",j);
            if (!strcmp(argv[a],ft)) {
               force = kTRUE;
               newcomp = j;
               ++ffirst;
               break;
            }
         }
         if (!force) {
            // Bad argument
            std::cerr << "Error: option " << argv[a] << " is not a supported option.\n";
            ++ffirst;
         }
      } else if (!outputPlace) {
         outputPlace = a;
      }
   }

   gSystem->Load("libTreePlayer");

   const char *targetname = 0;
   if (outputPlace) {
      targetname = argv[outputPlace];
   } else {
      targetname = argv[ffirst-1];
   }

   if (verbosity > 1) {
      std::cout << "hadd Target file: " << targetname << std::endl;
   }

   TFileMerger merger(kFALSE,kFALSE);
   merger.SetMsgPrefix("hadd");
   merger.SetPrintLevel(verbosity - 1);
   if (maxopenedfiles > 0) {
      merger.SetMaxOpenedFiles(maxopenedfiles);
   }
   if (!merger.OutputFile(targetname,force,newcomp) ) {
      std::cerr << "hadd error opening target file (does " << argv[ffirst-1] << " exist?)." << std::endl;
      std::cerr << "Pass \"-f\" argument to force re-creation of output file." << std::endl;
      exit(1);
   }

   for ( int i = ffirst; i < argc; i++ ) {
      if (argv[i] && argv[i][0]=='@') {
         std::ifstream indirect_file(argv[i]+1);
         if( ! indirect_file.is_open() ) {
            std::cerr<< "hadd could not open indirect file " << (argv[i]+1) << std::endl;
            return 1;
         }
         while( indirect_file ){
            std::string line;
            if( std::getline(indirect_file, line) && line.length() &&  !merger.AddFile(line.c_str()) ) {
               return 1;
            }
         }
      } else if( ! merger.AddFile(argv[i]) ) {
         if ( skip_errors ) {
            std::cerr << "hadd skipping file with error: " << argv[i] << std::endl;
         } else {
            std::cerr << "hadd exiting due to error in " << argv[i] << std::endl;
            return 1;
         }
      }
   }
   if (reoptimize) {
      merger.SetFastMethod(kFALSE);
   } else {
      if (merger.HasCompressionChange()) {
         // Don't warn if the user any request re-optimization.
         std::cout <<"hadd Sources and Target have different compression levels"<<std::endl;
         std::cout <<"hadd merging will be slower"<<std::endl;
      }
   }
   merger.SetNotrees(noTrees);
   Bool_t status = merger.Merge();

   if (status) {
      if (verbosity == 1) {
         std::cout << "hadd merged " << merger.GetMergeList()->GetEntries() << " input files in " << targetname << ".\n";
      }
      return 0;
   } else {
      if (verbosity == 1) {
         std::cout << "hadd failure during the merge of " << merger.GetMergeList()->GetEntries() << " input files in " << targetname << ".\n";
      }
      return 1;
   }
}
<commit_msg>Extend hadd -f to support compression setting.<commit_after>/*

  This program will add histograms (see note) and Trees from a list of root files and write them
  to a target root file. The target file is newly created and must not be
  identical to one of the source files.

  Syntax:

       hadd targetfile source1 source2 ...
    or
       hadd -f targetfile source1 source2 ...
         (targetfile is overwritten if it exists)

  When the -f option is specified, one can also specify the compression
  level of the target file. By default the compression level is 1, but
  if "-f0" is specified, the target file will not be compressed.
  if "-f6" is specified, the compression level 6 will be used.

  For example assume 3 files f1, f2, f3 containing histograms hn and Trees Tn
    f1 with h1 h2 h3 T1
    f2 with h1 h4 T1 T2
    f3 with h5
   the result of
     hadd -f x.root f1.root f2.root f3.root
   will be a file x.root with h1 h2 h3 h4 h5 T1 T2
   where h1 will be the sum of the 2 histograms in f1 and f2
         T1 will be the merge of the Trees in f1 and f2

   The files may contain sub-directories.

  if the source files contains histograms and Trees, one can skip
  the Trees with
       hadd -T targetfile source1 source2 ...

  Wildcarding and indirect files are also supported
    hadd result.root  myfil*.root
   will merge all files in myfil*.root
    hadd result.root file1.root @list.txt file2. root myfil*.root
    will merge file1. root, file2. root, all files in myfil*.root
    and all files in the indirect text file list.txt ("@" as the first
    character of the file indicates an indirect file. An indirect file
    is a text file containing a list of other files, including other
    indirect files, one line per file).

  If the sources and and target compression levels are identical (default),
  the program uses the TChain::Merge function with option "fast", ie
  the merge will be done without  unzipping or unstreaming the baskets
  (i.e. direct copy of the raw byte on disk). The "fast" mode is typically
  5 times faster than the mode unzipping and unstreaming the baskets.

  NOTE1: By default histograms are added. However hadd does not support the case where
         histograms have their bit TH1::kIsAverage set.

  NOTE2: hadd returns a status code: 0 if OK, -1 otherwise

  Authors: Rene Brun, Dirk Geppert, Sven A. Schmidt, sven.schmidt@cern.ch
         : rewritten from scratch by Rene Brun (30 November 2005)
            to support files with nested directories.
           Toby Burnett implemented the possibility to use indirect files.
 */

#include "RConfig.h"
#include <string>
#include "TFile.h"
#include "THashList.h"
#include "TKey.h"
#include "TObjString.h"
#include "Riostream.h"
#include "TClass.h"
#include "TSystem.h"
#include <stdlib.h>

#include "TFileMerger.h"

//___________________________________________________________________________
int main( int argc, char **argv )
{

   if ( argc < 3 || "-h" == std::string(argv[1]) || "--help" == std::string(argv[1]) ) {
      std::cout << "Usage: " << argv[0] << " [-f[0-9]] [-k] [-T] [-O] [-n maxopenedfiles] [-v verbosity] targetfile source1 [source2 source3 ...]" << std::endl;
      std::cout << "This program will add histograms from a list of root files and write them" << std::endl;
      std::cout << "to a target root file. The target file is newly created and must not " << std::endl;
      std::cout << "exist, or if -f (\"force\") is given, must not be one of the source files." << std::endl;
      std::cout << "Supply at least two source files for this to make sense... ;-)" << std::endl;
      std::cout << "If the option -k is used, hadd will not exit on corrupt or non-existant input files but skip the offending files instead." << std::endl;
      std::cout << "If the option -T is used, Trees are not merged" <<std::endl;
      std::cout << "If the option -O is used, when merging TTree, the basket size is re-optimized" <<std::endl;
      std::cout << "If the option -v is used, explicitly set the verbosity level; 0 request no output, 99 is the default" <<std::endl;
      std::cout << "If the option -n is used, hadd will open at most 'maxopenedfiles' at once, use 0 to request to use the system maximum." << std::endl;
      std::cout << "When -the -f option is specified, one can also specify the compression" <<std::endl;
      std::cout << "level of the target file. By default the compression level is 1, but" <<std::endl;
      std::cout << "if \"-f0\" is specified, the target file will not be compressed." <<std::endl;
      std::cout << "if \"-f6\" is specified, the compression level 6 will be used.  See  TFile::SetCompressionSettings for the support range of value." <<std::endl;
      std::cout << "if Target and source files have different compression settings"<<std::endl;
      std::cout << " a slower method is used"<<std::endl;
      return 1;
   }

   Bool_t force = kFALSE;
   Bool_t skip_errors = kFALSE;
   Bool_t reoptimize = kFALSE;
   Bool_t noTrees = kFALSE;
   Int_t maxopenedfiles = 0;
   Int_t verbosity = 99;

   int outputPlace = 0;
   int ffirst = 2;
   Int_t newcomp = 1;
   for( int a = 1; a < argc; ++a ) {
      if ( strcmp(argv[a],"-T") == 0 ) {
         noTrees = kTRUE;
         ++ffirst;
      } else if ( strcmp(argv[a],"-f") == 0 ) {
         force = kTRUE;
         ++ffirst;
      } else if ( strcmp(argv[a],"-k") == 0 ) {
         skip_errors = kTRUE;
         ++ffirst;
      } else if ( strcmp(argv[a],"-O") == 0 ) {
         reoptimize = kTRUE;
         ++ffirst;
      } else if ( strcmp(argv[a],"-n") == 0 ) {
         if (a+1 >= argc) {
            std::cerr << "Error: no maximum number of opened was provided after -n.\n";
         } else {
            Long_t request = strtol(argv[a+1], 0, 10);
            if (request < kMaxLong && request >= 0) {
               maxopenedfiles = (Int_t)request;
               ++a;
               ++ffirst;
            } else {
               std::cerr << "Error: could not parse the max number of opened file passed after -n: " << argv[a+1] << ". We will use the system maximum.\n";
            }
         }
         ++ffirst;
      } else if ( strcmp(argv[a],"-v") == 0 ) {
         if (a+1 >= argc) {
            std::cerr << "Error: no verbosity level was provided after -v.\n";
         } else {
            Long_t request = strtol(argv[a+1], 0, 10);
            if (request < kMaxLong && request >= 0) {
               verbosity = (Int_t)request;
               ++a;
               ++ffirst;
            } else {
               std::cerr << "Error: could not parse the verbosity level passed after -v: " << argv[a+1] << ". We will use the default value (99).\n";
            }
         }
         ++ffirst;
      } else if ( argv[a][0] == '-' ) {
         char ft[6];
         for ( int alg = 0; alg <= 2; ++alg ) {
            for( int j=0; j<=9; ++j ) {
               const int comp = (alg*100)+j;
               snprintf(ft,6,"-f%d",comp);
               if (!strcmp(argv[a],ft)) {
                  force = kTRUE;
                  newcomp = comp;
                  ++ffirst;
                  break;
               }
            }
         }
         if (!force) {
            // Bad argument
            std::cerr << "Error: option " << argv[a] << " is not a supported option.\n";
            ++ffirst;
         }
      } else if (!outputPlace) {
         outputPlace = a;
      }
   }

   gSystem->Load("libTreePlayer");

   const char *targetname = 0;
   if (outputPlace) {
      targetname = argv[outputPlace];
   } else {
      targetname = argv[ffirst-1];
   }

   if (verbosity > 1) {
      std::cout << "hadd Target file: " << targetname << std::endl;
   }

   TFileMerger merger(kFALSE,kFALSE);
   merger.SetMsgPrefix("hadd");
   merger.SetPrintLevel(verbosity - 1);
   if (maxopenedfiles > 0) {
      merger.SetMaxOpenedFiles(maxopenedfiles);
   }
   if (!merger.OutputFile(targetname,force,newcomp) ) {
      std::cerr << "hadd error opening target file (does " << argv[ffirst-1] << " exist?)." << std::endl;
      std::cerr << "Pass \"-f\" argument to force re-creation of output file." << std::endl;
      exit(1);
   }

   for ( int i = ffirst; i < argc; i++ ) {
      if (argv[i] && argv[i][0]=='@') {
         std::ifstream indirect_file(argv[i]+1);
         if( ! indirect_file.is_open() ) {
            std::cerr<< "hadd could not open indirect file " << (argv[i]+1) << std::endl;
            return 1;
         }
         while( indirect_file ){
            std::string line;
            if( std::getline(indirect_file, line) && line.length() &&  !merger.AddFile(line.c_str()) ) {
               return 1;
            }
         }
      } else if( ! merger.AddFile(argv[i]) ) {
         if ( skip_errors ) {
            std::cerr << "hadd skipping file with error: " << argv[i] << std::endl;
         } else {
            std::cerr << "hadd exiting due to error in " << argv[i] << std::endl;
            return 1;
         }
      }
   }
   if (reoptimize) {
      merger.SetFastMethod(kFALSE);
   } else {
      if (merger.HasCompressionChange()) {
         // Don't warn if the user any request re-optimization.
         std::cout <<"hadd Sources and Target have different compression levels"<<std::endl;
         std::cout <<"hadd merging will be slower"<<std::endl;
      }
   }
   merger.SetNotrees(noTrees);
   Bool_t status = merger.Merge();

   if (status) {
      if (verbosity == 1) {
         std::cout << "hadd merged " << merger.GetMergeList()->GetEntries() << " input files in " << targetname << ".\n";
      }
      return 0;
   } else {
      if (verbosity == 1) {
         std::cout << "hadd failure during the merge of " << merger.GetMergeList()->GetEntries() << " input files in " << targetname << ".\n";
      }
      return 1;
   }
}
<|endoftext|>


<commit_before>/*
This file is part of the ORK library.
Full copyright and license terms can be found in the LICENSE.txt file.
*/
#ifdef ORK_TEXT_HPP
#	error This header can only be included from ork/ork.hpp!
#endif

#ifndef ORK_TEXT_HPP
#define ORK_TEXT_HPP

#ifndef ORK_ORK_HPP
#	error This header can only be included from ork/ork.hpp!
#endif

#include<memory>
#include<string>

#if defined UNICODE || defined _UNICODE//Avoid checking both everywhere
#	define ORK_UNICODE 1
#else
#	define ORK_UNICODE 0
#endif

#if ORK_GCC || ORK_MSC >= 1900
#   define ORK_STL_HAS_FILE 1
#else
#   define ORK_STL_HAS_FILE 0
#endif

//Forward declaration to keep boost out of headers
#if ORK_STL_HAS_FILE

#include<iosfwd>

namespace ork {
namespace detail {
namespace fstream {

using wofstream = std::wofstream;
using wifstream = std::wifstream;
using wfstream = std::wfstream;
using ofstream = std::ofstream;
using ifstream = std::ifstream;
using fstream = std::fstream;

}//namespace fstream
}//namespace detail
}//namespace ork

#else

//Some boost configuration here
#define BOOST_SYSTEM_NO_DEPRECATED 1
#define BOOST_FILESYSTEM_NO_DEPRECATED 1

namespace boost {
namespace filesystem {

class wofstream;
class wifstream;
class wfstream;
class ofstream;
class ifstream;
class fstream;

}//filesystem
}//boost

namespace ork {
namespace detail {
namespace fstream {

using wofstream = boost::filesystem::wofstream;
using wifstream = boost::filesystem::wifstream;
using wfstream = boost::filesystem::wfstream;
using ofstream = boost::filesystem::ofstream;
using ifstream = boost::filesystem::ifstream;
using fstream = boost::filesystem::fstream;

}//namespace fstream
}//namespace detail
}//namespace ork

#endif

namespace ork {

#define ORK_STR_(X) #X
#define ORK_STR(X) ORK_STR_(X)

#define ORK_CAT_(X,Y) X##Y
#define ORK_CAT(X,Y) ORK_CAT_(X,Y)
#define ORK_CAT3(X,Y,Z) ORK_CAT(X,ORK_CAT(Y,Z))
#define ORK_CAT4(W,X,Y,Z) ORK_CAT(W,ORK_CAT3(X,Y,Z))
#define ORK_CAT5(V,W,X,Y,Z) ORK_CAT(V,ORK_CAT4(W,X,Y,Z))

#define ORK_WIDEN_(X) L##X
#define ORK_WIDEN(X) ORK_WIDEN_(X)

#if ORK_UNICODE
	typedef std::wstring string;
	typedef std::wstringstream string_stream;
	typedef wchar_t char_t;
	typedef std::wostream o_stream;
	typedef std::wistream i_stream;
	typedef std::wostringstream o_string_stream;
	typedef std::wistringstream i_string_stream;
	typedef std::wstreambuf streambuf;
	#define ORK(X) ORK_WIDEN(X)//The shortest prefixed macro
	#define ORK_CIN std::wcin
	#define ORK_COUT std::wcout
	#define ORK_CERR std::wcerr
	#define ORK_CLOG std::wclog
#else
	typedef std::string string;
	typedef std::stringstream string_stream;
	typedef char char_t;
	typedef std::ostream o_stream;
	typedef std::istream i_stream;
	typedef std::ostringstream o_string_stream;
	typedef std::istringstream i_string_stream;
	typedef std::streambuf streambuf;
	#define ORK(X) X//The shortest prefixed macro
	#define ORK_CIN std::cin
	#define ORK_COUT std::cout
	#define ORK_CERR std::cerr
	#define ORK_CLOG std::clog
#endif
typedef std::wstring wstring;
typedef std::string bstring;//Byte string, so that searches can identify unhandled unicode
typedef std::stringstream b_string_stream;
typedef std::ostream bo_stream;
typedef std::istream bi_stream;
typedef std::ostringstream bo_string_stream;
typedef std::istringstream bi_string_stream;
#define BORK(X) X//Byte Text, so that searches can identify unhandled unicode

#define ORK_FILEN ORK(__FILE__)
#define ORK_LINE ORK(ORK_STR(__LINE__))

#define _ORK_UID2(PREFIX, LINE, COUNT) ORK_CAT3(PREFIX, LINE, COUNT)
#define _ORK_UID1(PREFIX, LINE) _ORK_UID2(PREFIX, LINE, __COUNTER__)
#define ORK_UID(PREFIX) _ORK_UID1(PREFIX, __LINE__)

#if ORK_MSC
#   define ORK_FUNC __FUNCTION__
#elif ORK_GCC
#   define ORK_FUNC __PRETTY_FUNCTION__
#else
#	error Compiler not supported
#endif

#define ORK_FLOC ORK_CAT4(ORK_FILEN, ORK("("), ORK_LINE, ORK(")"))

#if ORK_UNICODE
	typedef detail::fstream::wofstream of_stream;
	typedef detail::fstream::wifstream if_stream;
	typedef detail::fstream::wfstream f_stream;
	#define ORK_GEN_STR generic_wstring
	#define ORK_STRING wstring
#else
	typedef detail::fstream::ofstream of_stream;
	typedef detail::fstream::ifstream if_stream;
	typedef detail::fstream::fstream f_stream;
	#define ORK_GEN_STR generic_string
	#define ORK_STRING string
#endif

class string_converter_type {//Just a thread-safe wrapper for std::wstring_convert
private:
	struct impl;
private:
	std::unique_ptr<impl>_pimpl;
public:
	string_converter_type();
	~string_converter_type();
	ORK_MOVE_ONLY(string_converter_type)
public:
	ORK_ORK_API bstring wide2byte(const wchar_t s);
	ORK_ORK_API bstring wide2byte(const wchar_t*s);
	ORK_ORK_API bstring wide2byte(const wstring&s);
	ORK_ORK_API bstring wide2byte(const wchar_t*first, const wchar_t*last);

	ORK_ORK_API wstring byte2wide(const char s);
	ORK_ORK_API wstring byte2wide(const char*s);
	ORK_ORK_API wstring byte2wide(const bstring&s);
	ORK_ORK_API wstring byte2wide(const char*first, const char*last);
};
ORK_ORK_EXT(string_converter_type&) g_string_converter();

#if ORK_UNICODE
#	define ORK_STR_2_BYTE(ORK_STRING) ork::g_string_converter().wide2byte(ORK_STRING)
#	define ORK_STR_2_WIDE(ORK_STRING) ORK_STRING
#	define ORK_BYTE_2_STR(ORK_STRING) ork::g_string_converter().byte2wide(ORK_STRING)
#	define ORK_WIDE_2_STR(ORK_STRING) ORK_STRING
#else
#	define ORK_STR_2_BYTE(ORK_STRING) ORK_STRING
#	define ORK_STR_2_WIDE(ORK_STRING) ork::g_string_converter().byte2wide(ORK_STRING)
#	define ORK_BYTE_2_STR(ORK_STRING) ORK_STRING
#	define ORK_WIDE_2_STR(ORK_STRING) ork::g_string_converter().wide2byte(ORK_STRING)
#endif

}//namespace ork

#endif//ORK_TEXT_HPP
<commit_msg>Added typedefs for byte and wide file streams<commit_after>/*
This file is part of the ORK library.
Full copyright and license terms can be found in the LICENSE.txt file.
*/
#ifdef ORK_TEXT_HPP
#	error This header can only be included from ork/ork.hpp!
#endif

#ifndef ORK_TEXT_HPP
#define ORK_TEXT_HPP

#ifndef ORK_ORK_HPP
#	error This header can only be included from ork/ork.hpp!
#endif

#include<memory>
#include<string>

#if defined UNICODE || defined _UNICODE//Avoid checking both everywhere
#	define ORK_UNICODE 1
#else
#	define ORK_UNICODE 0
#endif

#if ORK_GCC || ORK_MSC >= 1900
#   define ORK_STL_HAS_FILE 1
#else
#   define ORK_STL_HAS_FILE 0
#endif

//Forward declaration to keep boost out of headers
#if ORK_STL_HAS_FILE

#include<iosfwd>

namespace ork {
namespace detail {
namespace fstream {

using wofstream = std::wofstream;
using wifstream = std::wifstream;
using wfstream = std::wfstream;
using ofstream = std::ofstream;
using ifstream = std::ifstream;
using fstream = std::fstream;

}//namespace fstream
}//namespace detail
}//namespace ork

#else

//Some boost configuration here
#define BOOST_SYSTEM_NO_DEPRECATED 1
#define BOOST_FILESYSTEM_NO_DEPRECATED 1

namespace boost {
namespace filesystem {

class wofstream;
class wifstream;
class wfstream;
class ofstream;
class ifstream;
class fstream;

}//filesystem
}//boost

namespace ork {
namespace detail {
namespace fstream {

using wofstream = boost::filesystem::wofstream;
using wifstream = boost::filesystem::wifstream;
using wfstream = boost::filesystem::wfstream;
using ofstream = boost::filesystem::ofstream;
using ifstream = boost::filesystem::ifstream;
using fstream = boost::filesystem::fstream;

}//namespace fstream
}//namespace detail
}//namespace ork

#endif

namespace ork {

#define ORK_STR_(X) #X
#define ORK_STR(X) ORK_STR_(X)

#define ORK_CAT_(X,Y) X##Y
#define ORK_CAT(X,Y) ORK_CAT_(X,Y)
#define ORK_CAT3(X,Y,Z) ORK_CAT(X,ORK_CAT(Y,Z))
#define ORK_CAT4(W,X,Y,Z) ORK_CAT(W,ORK_CAT3(X,Y,Z))
#define ORK_CAT5(V,W,X,Y,Z) ORK_CAT(V,ORK_CAT4(W,X,Y,Z))

#define ORK_WIDEN_(X) L##X
#define ORK_WIDEN(X) ORK_WIDEN_(X)

#if ORK_UNICODE
	typedef std::wstring string;
	typedef std::wstringstream string_stream;
	typedef wchar_t char_t;
	typedef std::wostream o_stream;
	typedef std::wistream i_stream;
	typedef std::wostringstream o_string_stream;
	typedef std::wistringstream i_string_stream;
	typedef std::wstreambuf streambuf;
	#define ORK(X) ORK_WIDEN(X)//The shortest prefixed macro
	#define ORK_CIN std::wcin
	#define ORK_COUT std::wcout
	#define ORK_CERR std::wcerr
	#define ORK_CLOG std::wclog
#else
	typedef std::string string;
	typedef std::stringstream string_stream;
	typedef char char_t;
	typedef std::ostream o_stream;
	typedef std::istream i_stream;
	typedef std::ostringstream o_string_stream;
	typedef std::istringstream i_string_stream;
	typedef std::streambuf streambuf;
	#define ORK(X) X//The shortest prefixed macro
	#define ORK_CIN std::cin
	#define ORK_COUT std::cout
	#define ORK_CERR std::cerr
	#define ORK_CLOG std::clog
#endif
typedef std::wstring wstring;
typedef std::string bstring;//Byte string, so that searches can identify unhandled unicode
typedef std::stringstream b_string_stream;
typedef std::ostream bo_stream;
typedef std::istream bi_stream;
typedef std::ostringstream bo_string_stream;
typedef std::istringstream bi_string_stream;
#define BORK(X) X//Byte Text, so that searches can identify unhandled unicode

#define ORK_FILEN ORK(__FILE__)
#define ORK_LINE ORK(ORK_STR(__LINE__))

#define _ORK_UID2(PREFIX, LINE, COUNT) ORK_CAT3(PREFIX, LINE, COUNT)
#define _ORK_UID1(PREFIX, LINE) _ORK_UID2(PREFIX, LINE, __COUNTER__)
#define ORK_UID(PREFIX) _ORK_UID1(PREFIX, __LINE__)

#if ORK_MSC
#   define ORK_FUNC __FUNCTION__
#elif ORK_GCC
#   define ORK_FUNC __PRETTY_FUNCTION__
#else
#	error Compiler not supported
#endif

#define ORK_FLOC ORK_CAT4(ORK_FILEN, ORK("("), ORK_LINE, ORK(")"))

#if ORK_UNICODE
	typedef detail::fstream::wofstream of_stream;
	typedef detail::fstream::wifstream if_stream;
	typedef detail::fstream::wfstream f_stream;
	#define ORK_GEN_STR generic_wstring
	#define ORK_STRING wstring
#else
	typedef detail::fstream::ofstream of_stream;
	typedef detail::fstream::ifstream if_stream;
	typedef detail::fstream::fstream f_stream;
	#define ORK_GEN_STR generic_string
	#define ORK_STRING string
#endif
typedef detail::fstream::wofstream wof_stream;
typedef detail::fstream::wifstream wif_stream;
typedef detail::fstream::wfstream wf_stream;
typedef detail::fstream::ofstream bof_stream;
typedef detail::fstream::ifstream bif_stream;
typedef detail::fstream::fstream bf_stream;

class string_converter_type {//Just a thread-safe wrapper for std::wstring_convert
private:
	struct impl;
private:
	std::unique_ptr<impl>_pimpl;
public:
	string_converter_type();
	~string_converter_type();
	ORK_MOVE_ONLY(string_converter_type)
public:
	ORK_ORK_API bstring wide2byte(const wchar_t s);
	ORK_ORK_API bstring wide2byte(const wchar_t*s);
	ORK_ORK_API bstring wide2byte(const wstring&s);
	ORK_ORK_API bstring wide2byte(const wchar_t*first, const wchar_t*last);

	ORK_ORK_API wstring byte2wide(const char s);
	ORK_ORK_API wstring byte2wide(const char*s);
	ORK_ORK_API wstring byte2wide(const bstring&s);
	ORK_ORK_API wstring byte2wide(const char*first, const char*last);
};
ORK_ORK_EXT(string_converter_type&) g_string_converter();

#if ORK_UNICODE
#	define ORK_STR_2_BYTE(ORK_STRING) ork::g_string_converter().wide2byte(ORK_STRING)
#	define ORK_STR_2_WIDE(ORK_STRING) ORK_STRING
#	define ORK_BYTE_2_STR(ORK_STRING) ork::g_string_converter().byte2wide(ORK_STRING)
#	define ORK_WIDE_2_STR(ORK_STRING) ORK_STRING
#else
#	define ORK_STR_2_BYTE(ORK_STRING) ORK_STRING
#	define ORK_STR_2_WIDE(ORK_STRING) ork::g_string_converter().byte2wide(ORK_STRING)
#	define ORK_BYTE_2_STR(ORK_STRING) ORK_STRING
#	define ORK_WIDE_2_STR(ORK_STRING) ork::g_string_converter().wide2byte(ORK_STRING)
#endif

}//namespace ork

#endif//ORK_TEXT_HPP
<|endoftext|>


<commit_before>/*
Copyright (c) 2015, Dimitri Diakopoulos 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.

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.
*/

#if (_MSC_VER)
    #pragma warning (push)
    #pragma warning (disable: 181 111 4267 4996 4244 4701 4702 4133 4100 4127 4206 4312 4505 4365 4005 4013 4334)
#endif

#ifdef __clang__
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Wconversion"
    #pragma clang diagnostic ignored "-Wshadow"
    #pragma clang diagnostic ignored "-Wdeprecated-register"
#endif

#ifndef MODPLUG_STATIC
#define MODPLUG_STATIC
#endif

#include "libmodplug/src/load_669.cpp"
#include "libmodplug/src/load_abc.cpp"
#include "libmodplug/src/load_amf.cpp"
#include "libmodplug/src/load_ams.cpp"
#include "libmodplug/src/load_dbm.cpp"
#include "libmodplug/src/load_dmf.cpp"
#include "libmodplug/src/load_dsm.cpp"
#include "libmodplug/src/load_far.cpp"
#include "libmodplug/src/load_it.cpp"
#include "libmodplug/src/load_j2b.cpp"
#include "libmodplug/src/load_mdl.cpp"
#include "libmodplug/src/load_med.cpp"
#define none none_alt
#define MMFILE MMFILE_alt
#define mmfseek mmfseek_alt
#define mmftell mmftell_alt
#define mmreadUBYTES mmreadUBYTES_alt
#include "libmodplug/src/load_mid.cpp"
#include "libmodplug/src/load_mod.cpp"
#include "libmodplug/src/load_mt2.cpp"
#include "libmodplug/src/load_mtm.cpp"
#include "libmodplug/src/load_okt.cpp"
#define MMFILE MMFILE_alt2
#define mmfseek mmfseek_alt2
#define mmftell mmftell_alt2
#define mmreadUBYTES mmreadUBYTES_alt2
#include "libmodplug/src/load_pat.cpp"
#include "libmodplug/src/load_psm.cpp"
#include "libmodplug/src/load_ptm.cpp"
#include "libmodplug/src/load_s3m.cpp"
#include "libmodplug/src/load_stm.cpp"
#include "libmodplug/src/load_ult.cpp"
#include "libmodplug/src/load_umx.cpp"
#include "libmodplug/src/load_wav.cpp"
#include "libmodplug/src/load_xm.cpp"
#include "libmodplug/src/mmcmp.cpp"
#include "libmodplug/src/modplug.cpp"
#include "libmodplug/src/sndfile.cpp"
#include "libmodplug/src/sndmix.cpp"
#include "libmodplug/src/fastmix.cpp"
#include "libmodplug/src/snd_dsp.cpp"
#include "libmodplug/src/snd_flt.cpp"
#include "libmodplug/src/snd_fx.cpp"

#ifdef __clang__
    #pragma clang diagnostic pop
#endif

#if (_MSC_VER)
    #pragma warning (pop)
#endif
<commit_msg>update for osx/clang compile<commit_after>/*
Copyright (c) 2016, Dimitri Diakopoulos 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.

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.
*/

#if (_MSC_VER)
    #pragma warning (push)
    #pragma warning (disable: 181 111 4267 4996 4244 4701 4702 4133 4100 4127 4206 4312 4505 4365 4005 4013 4334)
#endif

#ifdef __clang__
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Wconversion"
    #pragma clang diagnostic ignored "-Wshadow"
    #pragma clang diagnostic ignored "-Wdeprecated-register"
#endif

#ifndef MODPLUG_STATIC
#define MODPLUG_STATIC
#endif

#define HAVE_SINF
#define HAVE_SETENV

#include "libmodplug/src/load_669.cpp"
#include "libmodplug/src/load_abc.cpp"
#include "libmodplug/src/load_amf.cpp"
#include "libmodplug/src/load_ams.cpp"
#include "libmodplug/src/load_dbm.cpp"
#include "libmodplug/src/load_dmf.cpp"
#include "libmodplug/src/load_dsm.cpp"
#include "libmodplug/src/load_far.cpp"
#include "libmodplug/src/load_it.cpp"
#include "libmodplug/src/load_j2b.cpp"
#include "libmodplug/src/load_mdl.cpp"
#include "libmodplug/src/load_med.cpp"

#define none none_alt
#define MMFILE MMFILE_alt
#define mmfseek mmfseek_alt
#define mmftell mmftell_alt
#define mmreadUBYTES mmreadUBYTES_alt
#include "libmodplug/src/load_mid.cpp"
#include "libmodplug/src/load_mod.cpp"
#include "libmodplug/src/load_mt2.cpp"
#include "libmodplug/src/load_mtm.cpp"
#include "libmodplug/src/load_okt.cpp"
#undef MMFILE
#undef mmfseek
#undef mmftell
#undef mmreadUBYTES

#define MMFILE MMFILE_alt2
#define mmfseek mmfseek_alt2
#define mmftell mmftell_alt2
#define mmreadUBYTES mmreadUBYTES_alt2
#include "libmodplug/src/load_pat.cpp"
#include "libmodplug/src/load_psm.cpp"
#include "libmodplug/src/load_ptm.cpp"
#include "libmodplug/src/load_s3m.cpp"
#include "libmodplug/src/load_stm.cpp"
#include "libmodplug/src/load_ult.cpp"
#include "libmodplug/src/load_umx.cpp"
#include "libmodplug/src/load_wav.cpp"
#include "libmodplug/src/load_xm.cpp"

#include "libmodplug/src/mmcmp.cpp"
#include "libmodplug/src/modplug.cpp"
#include "libmodplug/src/sndfile.cpp"
#include "libmodplug/src/sndmix.cpp"
#include "libmodplug/src/fastmix.cpp"
#include "libmodplug/src/snd_dsp.cpp"
#include "libmodplug/src/snd_flt.cpp"
#include "libmodplug/src/snd_fx.cpp"

#ifdef __clang__
    #pragma clang diagnostic pop
#endif

#if (_MSC_VER)
    #pragma warning (pop)
#endif
<|endoftext|>


<commit_before>/*

  This program will add histograms (see note) and Trees from a list of root files and write them
  to a target root file. The target file is newly created and must not be
  identical to one of the source files.

  Syntax:

       hadd targetfile source1 source2 ...
    or
       hadd -f targetfile source1 source2 ...
         (targetfile is overwritten if it exists)

  When -the -f option is specified, one can also specify the compression
  level of the target file. By default the compression level is 1, but
  if "-f0" is specified, the target file will not be compressed.
  if "-f6" is specified, the compression level 6 will be used.

  For example assume 3 files f1, f2, f3 containing histograms hn and Trees Tn
    f1 with h1 h2 h3 T1
    f2 with h1 h4 T1 T2
    f3 with h5
   the result of
     hadd -f x.root f1.root f2.root f3.root
   will be a file x.root with h1 h2 h3 h4 h5 T1 T2
   where h1 will be the sum of the 2 histograms in f1 and f2
         T1 will be the merge of the Trees in f1 and f2

   The files may contain sub-directories.

  if the source files contains histograms and Trees, one can skip
  the Trees with
       hadd -T targetfile source1 source2 ...

  Wildcarding and indirect files are also supported
    hadd result.root  myfil*.root
   will merge all files in myfil*.root
    hadd result.root file1.root @list.txt file2. root myfil*.root
    will merge file1. root, file2. root, all files in myfil*.root
    and all files in the indirect text file list.txt ("@" as the first
    character of the file indicates an indirect file. An indirect file
    is a text file containing a list of other files, including other
    indirect files, one line per file).

  If the sources and and target compression levels are identical (default),
  the program uses the TChain::Merge function with option "fast", ie
  the merge will be done without  unzipping or unstreaming the baskets
  (i.e. direct copy of the raw byte on disk). The "fast" mode is typically
  5 times faster than the mode unzipping and unstreaming the baskets.

  NOTE1: By default histograms are added. However if histograms have their bit kIsAverage
        set, the contents are averaged instead of being summed. See TH1::Add.

  NOTE2: hadd returns a status code: 0 if OK, -1 otherwise

  Authors: Rene Brun, Dirk Geppert, Sven A. Schmidt, sven.schmidt@cern.ch
         : rewritten from scratch by Rene Brun (30 November 2005)
            to support files with nested directories.
           Toby Burnett implemented the possibility to use indirect files.
 */

#include "RConfig.h"
#include <string>
#include "TChain.h"
#include "TFile.h"
#include "THashList.h"
#include "TH1.h"
#include "TKey.h"
#include "TObjString.h"
#include "Riostream.h"
#include "TClass.h"
#include "TSystem.h"
#include <stdlib.h>

TList *FileList;
TFile *Target, *Source;
Bool_t noTrees;
Bool_t fastMethod;

int AddFile(TList* sourcelist, std::string entry, int newcomp) ;
int MergeRootfile( TDirectory *target, TList *sourcelist, Int_t isdir );

//___________________________________________________________________________
int main( int argc, char **argv )
{

   if ( argc < 3 || "-h" == string(argv[1]) || "--help" == string(argv[1]) ) {
      cout << "Usage: " << argv[0] << " [-f] [-T] targetfile source1 [source2 source3 ...]" << endl;
      cout << "This program will add histograms from a list of root files and write them" << endl;
      cout << "to a target root file. The target file is newly created and must not " << endl;
      cout << "exist, or if -f (\"force\") is given, must not be one of the source files." << endl;
      cout << "Supply at least two source files for this to make sense... ;-)" << endl;
      cout << "If the first argument is -T, Trees are not merged" <<endl;
      cout << "When -the -f option is specified, one can also specify the compression" <<endl;
      cout << "level of the target file. By default the compression level is 1, but" <<endl;
      cout << "if \"-f0\" is specified, the target file will not be compressed." <<endl;
      cout << "if \"-f6\" is specified, the compression level 6 will be used." <<endl;
      cout << "if Target and source files have different compression levels"<<endl;
      cout << " a slower method is used"<<endl;
      return 1;
   }
   FileList = new TList();

   Bool_t force = (!strcmp(argv[1],"-f") || !strcmp(argv[2],"-f"));
   noTrees = (!strcmp(argv[1],"-T") || !strcmp(argv[2],"-T"));
   Int_t newcomp = 1;
   char ft[4];
   for (int j=0;j<=9;j++) {
      sprintf(ft,"-f%d",j);
      if (!strcmp(argv[1],ft) || !strcmp(argv[2],ft)) {
         force = kTRUE;
         newcomp = j;
         break;
      }
   }

   gSystem->Load("libTreePlayer");

   int ffirst = 2;
   if (force) ffirst++;
   if (noTrees) ffirst++;

   cout << "Target file: " << argv[ffirst-1] << endl;

   Target = TFile::Open( argv[ffirst-1], (force?"RECREATE":"CREATE") );
   if (!Target || Target->IsZombie()) {
      cerr << "Error opening target file (does " << argv[ffirst-1] << " exist?)." << endl;
      cerr << "Pass \"-f\" argument to force re-creation of output file." << endl;
      exit(1);
   }
   Target->SetCompressionLevel(newcomp);

   // by default hadd can merge Trees in a file that can go up to 100 Gbytes
   Long64_t maxsize = 100000000; //100GB
   maxsize *= 100;  //to bypass some compiler limitations with big constants
   TTree::SetMaxTreeSize(maxsize);

   fastMethod = kTRUE;
   for ( int i = ffirst; i < argc; i++ ) {
      if( AddFile(FileList, argv[i], newcomp) !=0 ) return 1;
   }
   if (!fastMethod) {
      cout <<"Sources and Target have different compression levels"<<endl;
      cout <<"Merging will be slower"<<endl;
   }

   int status = MergeRootfile( Target, FileList,0 );

   //must delete Target to avoid a problem with dictionaries in~ TROOT
   delete Target;

   return status;
}

//___________________________________________________________________________
int AddFile(TList* sourcelist, std::string entry, int newcomp)
{
   // add a new file to the list of files
   static int count(0);
   if( entry.empty() ) return 0;
   size_t j =entry.find_first_not_of(' ');
   if( j==std::string::npos ) return 0;
   entry = entry.substr(j);
   if( entry.substr(0,1)=="@"){
      std::ifstream indirect_file(entry.substr(1).c_str() );
      if( ! indirect_file.is_open() ) {
         std::cerr<< "Could not open indirect file " << entry.substr(1) << std::endl;
         return 1;
      }
      while( indirect_file ){
         std::string line;
         std::getline(indirect_file, line);
         if( AddFile(sourcelist, line, newcomp)!=0 )return 1;;
      }
      return 0;
   }
   cout << "Source file " << (++count) << ": " << entry << endl;

   TFile* source = TFile::Open( entry.c_str());
   if( source==0 ){
      return 1;
   }
   sourcelist->Add(source);
   if (newcomp != source->GetCompressionLevel())  fastMethod = kFALSE;
   return 0;
}

//___________________________________________________________________________
int MergeRootfile( TDirectory *target, TList *sourcelist, Int_t isdir )
{
   // Merge all objects in a directory
   int status = 0;
   cout << "Target path: " << target->GetPath() << endl;
   TString path( (char*)strstr( target->GetPath(), ":" ) );
   path.Remove( 0, 2 );

   TDirectory *first_source = (TDirectory*)sourcelist->First();
   Int_t nguess = sourcelist->GetSize()+1000;
   THashList allNames(nguess);
   ((THashList*)target->GetList())->Rehash(nguess);
   ((THashList*)target->GetListOfKeys())->Rehash(nguess);
   TList listH;
   TString listHargs;
   listHargs.Form("((TCollection*)0x%lx)",&listH);
   while(first_source) {
      TDirectory *current_sourcedir = first_source->GetDirectory(path);
      if (!current_sourcedir) {
         first_source = (TDirectory*)sourcelist->After(first_source);
         continue;
      }

      // loop over all keys in this directory
      TChain *globChain = 0;
      TIter nextkey( current_sourcedir->GetListOfKeys() );
      TKey *key, *oldkey=0;
      //gain time, do not add the objects in the list in memory
      TH1::AddDirectory(kFALSE);

      while ( (key = (TKey*)nextkey())) {
         if (current_sourcedir == target) break;
         //keep only the highest cycle number for each key
         if (oldkey && !strcmp(oldkey->GetName(),key->GetName())) continue;
         if (!strcmp(key->GetClassName(),"TProcessID")) {key->ReadObj(); continue;}
         if (allNames.FindObject(key->GetName())) continue;
         TClass *cl = TClass::GetClass(key->GetClassName());
         if (!cl || !cl->InheritsFrom(TObject::Class())) {
            cout << "Cannot merge object type, name: "
                 << key->GetName() << " title: " << key->GetTitle() << endl;
            continue;
         }
         allNames.Add(new TObjString(key->GetName()));
         // read object from first source file
         //current_sourcedir->cd();
         TObject *obj = key->ReadObj();
         //printf("keyname=%s, obj=%x\n",key->GetName(),obj);

         if ( obj->IsA()->InheritsFrom( "TTree" ) ) {

            // loop over all source files create a chain of Trees "globChain"
            if (!noTrees) {
               TString obj_name;
               if (path.Length()) {
                  obj_name = path + "/" + obj->GetName();
               } else {
                  obj_name = obj->GetName();
               }
               globChain = new TChain(obj_name);
               globChain->Add(first_source->GetName());
               TFile *nextsource = (TFile*)sourcelist->After( first_source );
               while ( nextsource ) {
                  //do not add to the list a file that does not contain this Tree
                  TFile *curf = TFile::Open(nextsource->GetName());
                  if (curf) {
                     Bool_t mustAdd = kFALSE;
                     if (curf->FindKey(obj_name)) {
                        mustAdd = kTRUE;
                     } else {
                        //we could be more clever here. No need to import the object
                        //we are missing a function in TDirectory
                        TObject *aobj = curf->Get(obj_name);
                        if (aobj) { mustAdd = kTRUE; delete aobj;}
                     }
                     if (mustAdd) {
                        globChain->Add(nextsource->GetName());
                     }
                  }
                  delete curf;
                  nextsource = (TFile*)sourcelist->After( nextsource );
               }
            }
         } else if ( obj->IsA()->InheritsFrom( "TDirectory" ) ) {
            // it's a subdirectory

            cout << "Found subdirectory " << obj->GetName() << endl;
            // create a new subdir of same name and title in the target file
            target->cd();
            TDirectory *newdir = target->mkdir( obj->GetName(), obj->GetTitle() );

            // newdir is now the starting point of another round of merging
            // newdir still knows its depth within the target file via
            // GetPath(), so we can still figure out where we are in the recursion
            status = MergeRootfile( newdir, sourcelist,1);
            if (status) return status;

         } else if ( obj->InheritsFrom(TObject::Class())
              && obj->IsA()->GetMethodWithPrototype("Merge", "TCollection*") ) {
            // object implements Merge(TCollection*)

            // loop over all source files and merge same-name object
            TFile *nextsource = (TFile*)sourcelist->After( first_source );
            while ( nextsource ) {
               // make sure we are at the correct directory level by cd'ing to path
               TDirectory *ndir = nextsource->GetDirectory(path);
               if (ndir) {
                  ndir->cd();
                  TKey *key2 = (TKey*)gDirectory->GetListOfKeys()->FindObject(key->GetName());
                  if (key2) {
                     TObject *hobj = key2->ReadObj();
                     hobj->ResetBit(kMustCleanup);
                     listH.Add(hobj);
                     Int_t error = 0;
                     obj->Execute("Merge", listHargs.Data(), &error);
                     if (error) {
                        cerr << "Error calling Merge() on " << obj->GetName()
                             << " with the corresponding object in " << nextsource->GetName() << endl;
                     }
                     listH.Delete();
                  }
               }
               nextsource = (TFile*)sourcelist->After( nextsource );
            }
         } else {
            // object is of no type that we can merge
            cout << "Cannot merge object type, name: "
                 << obj->GetName() << " title: " << obj->GetTitle() << endl;

            // loop over all source files and write similar objects directly to the output file
            TFile *nextsource = (TFile*)sourcelist->After( first_source );
            while ( nextsource ) {
               // make sure we are at the correct directory level by cd'ing to path
               TDirectory *ndir = nextsource->GetDirectory(path);
               if (ndir) {
                  ndir->cd();
                  TKey *key2 = (TKey*)gDirectory->GetListOfKeys()->FindObject(key->GetName());
                  if (key2) {
                     TObject *nobj = key2->ReadObj();
                     nobj->ResetBit(kMustCleanup);
                     int nbytes1 = target->WriteTObject(nobj, key2->GetName(), "SingleKey" );
                     if (nbytes1 <= 0) status = -1;
                     delete nobj;
                  }
               }
               nextsource = (TFile*)sourcelist->After( nextsource );
            }
         }

         // now write the merged histogram (which is "in" obj) to the target file
         // note that this will just store obj in the current directory level,
         // which is not persistent until the complete directory itself is stored
         // by "target->Write()" below
         if ( obj ) {
            target->cd();

            //!!if the object is a tree, it is stored in globChain...
            if(obj->IsA()->InheritsFrom( "TDirectory" )) {
               //printf("cas d'une directory\n");
            } else if(obj->IsA()->InheritsFrom( "TTree" )) {
               if (!noTrees) {
                  globChain->ls();
                  if (fastMethod) globChain->Merge(target->GetFile(),0,"keep fast");
                  else            globChain->Merge(target->GetFile(),0,"keep");
                  delete globChain;
               }
            } else {
               int nbytes2 = obj->Write( key->GetName(), TObject::kSingleKey );
               if (nbytes2 <= 0) status = -1;
            }
         }
         oldkey = key;
         delete obj;
      } // while ( ( TKey *key = (TKey*)nextkey() ) )
      first_source = (TDirectory*)sourcelist->After(first_source);
   }
   // save modifications to target file
   target->SaveSelf(kTRUE);
   if (!isdir) sourcelist->Remove(sourcelist->First());
   return status;
}
<commit_msg>Remove the argument isdir from the function MergeRootFile. Do not remove the first file in the list when returning from MergeRootFile.<commit_after>/*

  This program will add histograms (see note) and Trees from a list of root files and write them
  to a target root file. The target file is newly created and must not be
  identical to one of the source files.

  Syntax:

       hadd targetfile source1 source2 ...
    or
       hadd -f targetfile source1 source2 ...
         (targetfile is overwritten if it exists)

  When -the -f option is specified, one can also specify the compression
  level of the target file. By default the compression level is 1, but
  if "-f0" is specified, the target file will not be compressed.
  if "-f6" is specified, the compression level 6 will be used.

  For example assume 3 files f1, f2, f3 containing histograms hn and Trees Tn
    f1 with h1 h2 h3 T1
    f2 with h1 h4 T1 T2
    f3 with h5
   the result of
     hadd -f x.root f1.root f2.root f3.root
   will be a file x.root with h1 h2 h3 h4 h5 T1 T2
   where h1 will be the sum of the 2 histograms in f1 and f2
         T1 will be the merge of the Trees in f1 and f2

   The files may contain sub-directories.

  if the source files contains histograms and Trees, one can skip
  the Trees with
       hadd -T targetfile source1 source2 ...

  Wildcarding and indirect files are also supported
    hadd result.root  myfil*.root
   will merge all files in myfil*.root
    hadd result.root file1.root @list.txt file2. root myfil*.root
    will merge file1. root, file2. root, all files in myfil*.root
    and all files in the indirect text file list.txt ("@" as the first
    character of the file indicates an indirect file. An indirect file
    is a text file containing a list of other files, including other
    indirect files, one line per file).

  If the sources and and target compression levels are identical (default),
  the program uses the TChain::Merge function with option "fast", ie
  the merge will be done without  unzipping or unstreaming the baskets
  (i.e. direct copy of the raw byte on disk). The "fast" mode is typically
  5 times faster than the mode unzipping and unstreaming the baskets.

  NOTE1: By default histograms are added. However if histograms have their bit kIsAverage
        set, the contents are averaged instead of being summed. See TH1::Add.

  NOTE2: hadd returns a status code: 0 if OK, -1 otherwise

  Authors: Rene Brun, Dirk Geppert, Sven A. Schmidt, sven.schmidt@cern.ch
         : rewritten from scratch by Rene Brun (30 November 2005)
            to support files with nested directories.
           Toby Burnett implemented the possibility to use indirect files.
 */

#include "RConfig.h"
#include <string>
#include "TChain.h"
#include "TFile.h"
#include "THashList.h"
#include "TH1.h"
#include "TKey.h"
#include "TObjString.h"
#include "Riostream.h"
#include "TClass.h"
#include "TSystem.h"
#include <stdlib.h>

TList *FileList;
TFile *Target, *Source;
Bool_t noTrees;
Bool_t fastMethod;

int AddFile(TList* sourcelist, std::string entry, int newcomp) ;
int MergeRootfile( TDirectory *target, TList *sourcelist);

//___________________________________________________________________________
int main( int argc, char **argv )
{

   if ( argc < 3 || "-h" == string(argv[1]) || "--help" == string(argv[1]) ) {
      cout << "Usage: " << argv[0] << " [-f] [-T] targetfile source1 [source2 source3 ...]" << endl;
      cout << "This program will add histograms from a list of root files and write them" << endl;
      cout << "to a target root file. The target file is newly created and must not " << endl;
      cout << "exist, or if -f (\"force\") is given, must not be one of the source files." << endl;
      cout << "Supply at least two source files for this to make sense... ;-)" << endl;
      cout << "If the first argument is -T, Trees are not merged" <<endl;
      cout << "When -the -f option is specified, one can also specify the compression" <<endl;
      cout << "level of the target file. By default the compression level is 1, but" <<endl;
      cout << "if \"-f0\" is specified, the target file will not be compressed." <<endl;
      cout << "if \"-f6\" is specified, the compression level 6 will be used." <<endl;
      cout << "if Target and source files have different compression levels"<<endl;
      cout << " a slower method is used"<<endl;
      return 1;
   }
   FileList = new TList();

   Bool_t force = (!strcmp(argv[1],"-f") || !strcmp(argv[2],"-f"));
   noTrees = (!strcmp(argv[1],"-T") || !strcmp(argv[2],"-T"));
   Int_t newcomp = 1;
   char ft[4];
   for (int j=0;j<=9;j++) {
      sprintf(ft,"-f%d",j);
      if (!strcmp(argv[1],ft) || !strcmp(argv[2],ft)) {
         force = kTRUE;
         newcomp = j;
         break;
      }
   }

   gSystem->Load("libTreePlayer");

   int ffirst = 2;
   if (force) ffirst++;
   if (noTrees) ffirst++;

   cout << "Target file: " << argv[ffirst-1] << endl;

   Target = TFile::Open( argv[ffirst-1], (force?"RECREATE":"CREATE") );
   if (!Target || Target->IsZombie()) {
      cerr << "Error opening target file (does " << argv[ffirst-1] << " exist?)." << endl;
      cerr << "Pass \"-f\" argument to force re-creation of output file." << endl;
      exit(1);
   }
   Target->SetCompressionLevel(newcomp);

   // by default hadd can merge Trees in a file that can go up to 100 Gbytes
   Long64_t maxsize = 100000000; //100GB
   maxsize *= 100;  //to bypass some compiler limitations with big constants
   TTree::SetMaxTreeSize(maxsize);

   fastMethod = kTRUE;
   for ( int i = ffirst; i < argc; i++ ) {
      if( AddFile(FileList, argv[i], newcomp) !=0 ) return 1;
   }
   if (!fastMethod) {
      cout <<"Sources and Target have different compression levels"<<endl;
      cout <<"Merging will be slower"<<endl;
   }

   int status = MergeRootfile( Target, FileList);

   //must delete Target to avoid a problem with dictionaries in~ TROOT
   delete Target;

   return status;
}

//___________________________________________________________________________
int AddFile(TList* sourcelist, std::string entry, int newcomp)
{
   // add a new file to the list of files
   static int count(0);
   if( entry.empty() ) return 0;
   size_t j =entry.find_first_not_of(' ');
   if( j==std::string::npos ) return 0;
   entry = entry.substr(j);
   if( entry.substr(0,1)=="@"){
      std::ifstream indirect_file(entry.substr(1).c_str() );
      if( ! indirect_file.is_open() ) {
         std::cerr<< "Could not open indirect file " << entry.substr(1) << std::endl;
         return 1;
      }
      while( indirect_file ){
         std::string line;
         std::getline(indirect_file, line);
         if( AddFile(sourcelist, line, newcomp)!=0 )return 1;;
      }
      return 0;
   }
   cout << "Source file " << (++count) << ": " << entry << endl;

   TFile* source = TFile::Open( entry.c_str());
   if( source==0 ){
      return 1;
   }
   sourcelist->Add(source);
   if (newcomp != source->GetCompressionLevel())  fastMethod = kFALSE;
   return 0;
}

//___________________________________________________________________________
int MergeRootfile( TDirectory *target, TList *sourcelist)
{
   // Merge all objects in a directory
   int status = 0;
   cout << "Target path: " << target->GetPath() << endl;
   TString path( (char*)strstr( target->GetPath(), ":" ) );
   path.Remove( 0, 2 );

   TDirectory *first_source = (TDirectory*)sourcelist->First();
   Int_t nguess = sourcelist->GetSize()+1000;
   THashList allNames(nguess);
   ((THashList*)target->GetList())->Rehash(nguess);
   ((THashList*)target->GetListOfKeys())->Rehash(nguess);
   TList listH;
   TString listHargs;
   listHargs.Form("((TCollection*)0x%lx)",&listH);
   while(first_source) {
      TDirectory *current_sourcedir = first_source->GetDirectory(path);
      if (!current_sourcedir) {
         first_source = (TDirectory*)sourcelist->After(first_source);
         continue;
      }

      // loop over all keys in this directory
      TChain *globChain = 0;
      TIter nextkey( current_sourcedir->GetListOfKeys() );
      TKey *key, *oldkey=0;
      //gain time, do not add the objects in the list in memory
      TH1::AddDirectory(kFALSE);

      while ( (key = (TKey*)nextkey())) {
         if (current_sourcedir == target) break;
         //keep only the highest cycle number for each key
         if (oldkey && !strcmp(oldkey->GetName(),key->GetName())) continue;
         if (!strcmp(key->GetClassName(),"TProcessID")) {key->ReadObj(); continue;}
         if (allNames.FindObject(key->GetName())) continue;
         TClass *cl = TClass::GetClass(key->GetClassName());
         if (!cl || !cl->InheritsFrom(TObject::Class())) {
            cout << "Cannot merge object type, name: "
                 << key->GetName() << " title: " << key->GetTitle() << endl;
            continue;
         }
         allNames.Add(new TObjString(key->GetName()));
         // read object from first source file
         //current_sourcedir->cd();
         TObject *obj = key->ReadObj();
         //printf("keyname=%s, obj=%x\n",key->GetName(),obj);

         if ( obj->IsA()->InheritsFrom( "TTree" ) ) {

            // loop over all source files create a chain of Trees "globChain"
            if (!noTrees) {
               TString obj_name;
               if (path.Length()) {
                  obj_name = path + "/" + obj->GetName();
               } else {
                  obj_name = obj->GetName();
               }
               globChain = new TChain(obj_name);
               globChain->Add(first_source->GetName());
               TFile *nextsource = (TFile*)sourcelist->After( first_source );
               while ( nextsource ) {
                  //do not add to the list a file that does not contain this Tree
                  TFile *curf = TFile::Open(nextsource->GetName());
                  if (curf) {
                     Bool_t mustAdd = kFALSE;
                     if (curf->FindKey(obj_name)) {
                        mustAdd = kTRUE;
                     } else {
                        //we could be more clever here. No need to import the object
                        //we are missing a function in TDirectory
                        TObject *aobj = curf->Get(obj_name);
                        if (aobj) { mustAdd = kTRUE; delete aobj;}
                     }
                     if (mustAdd) {
                        globChain->Add(nextsource->GetName());
                     }
                  }
                  delete curf;
                  nextsource = (TFile*)sourcelist->After( nextsource );
               }
            }
         } else if ( obj->IsA()->InheritsFrom( "TDirectory" ) ) {
            // it's a subdirectory

            cout << "Found subdirectory " << obj->GetName() << endl;
            // create a new subdir of same name and title in the target file
            target->cd();
            TDirectory *newdir = target->mkdir( obj->GetName(), obj->GetTitle() );

            // newdir is now the starting point of another round of merging
            // newdir still knows its depth within the target file via
            // GetPath(), so we can still figure out where we are in the recursion
            status = MergeRootfile( newdir, sourcelist);
            if (status) return status;

         } else if ( obj->InheritsFrom(TObject::Class())
              && obj->IsA()->GetMethodWithPrototype("Merge", "TCollection*") ) {
            // object implements Merge(TCollection*)

            // loop over all source files and merge same-name object
            TFile *nextsource = (TFile*)sourcelist->After( first_source );
            while ( nextsource ) {
               // make sure we are at the correct directory level by cd'ing to path
               TDirectory *ndir = nextsource->GetDirectory(path);
               if (ndir) {
                  ndir->cd();
                  TKey *key2 = (TKey*)gDirectory->GetListOfKeys()->FindObject(key->GetName());
                  if (key2) {
                     TObject *hobj = key2->ReadObj();
                     hobj->ResetBit(kMustCleanup);
                     listH.Add(hobj);
                     Int_t error = 0;
                     obj->Execute("Merge", listHargs.Data(), &error);
                     if (error) {
                        cerr << "Error calling Merge() on " << obj->GetName()
                             << " with the corresponding object in " << nextsource->GetName() << endl;
                     }
                     listH.Delete();
                  }
               }
               nextsource = (TFile*)sourcelist->After( nextsource );
            }
         } else {
            // object is of no type that we can merge
            cout << "Cannot merge object type, name: "
                 << obj->GetName() << " title: " << obj->GetTitle() << endl;

            // loop over all source files and write similar objects directly to the output file
            TFile *nextsource = (TFile*)sourcelist->After( first_source );
            while ( nextsource ) {
               // make sure we are at the correct directory level by cd'ing to path
               TDirectory *ndir = nextsource->GetDirectory(path);
               if (ndir) {
                  ndir->cd();
                  TKey *key2 = (TKey*)gDirectory->GetListOfKeys()->FindObject(key->GetName());
                  if (key2) {
                     TObject *nobj = key2->ReadObj();
                     nobj->ResetBit(kMustCleanup);
                     int nbytes1 = target->WriteTObject(nobj, key2->GetName(), "SingleKey" );
                     if (nbytes1 <= 0) status = -1;
                     delete nobj;
                  }
               }
               nextsource = (TFile*)sourcelist->After( nextsource );
            }
         }

         // now write the merged histogram (which is "in" obj) to the target file
         // note that this will just store obj in the current directory level,
         // which is not persistent until the complete directory itself is stored
         // by "target->Write()" below
         if ( obj ) {
            target->cd();

            //!!if the object is a tree, it is stored in globChain...
            if(obj->IsA()->InheritsFrom( "TDirectory" )) {
               //printf("cas d'une directory\n");
            } else if(obj->IsA()->InheritsFrom( "TTree" )) {
               if (!noTrees) {
                  globChain->ls();
                  if (fastMethod) globChain->Merge(target->GetFile(),0,"keep fast");
                  else            globChain->Merge(target->GetFile(),0,"keep");
                  delete globChain;
               }
            } else {
               int nbytes2 = obj->Write( key->GetName(), TObject::kSingleKey );
               if (nbytes2 <= 0) status = -1;
            }
         }
         oldkey = key;
         delete obj;
      } // while ( ( TKey *key = (TKey*)nextkey() ) )
      first_source = (TDirectory*)sourcelist->After(first_source);
   }
   // save modifications to target file
   target->SaveSelf(kTRUE);
   return status;
}
<|endoftext|>


<commit_before>// LAF OS Library
// Copyright (C) 2020  Igara Studio S.A.
// Copyright (C) 2016-2017  David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.

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

#include "os/win/wintab.h"

#include "base/convert_to.h"
#include "base/debug.h"
#include "base/fs.h"
#include "base/log.h"
#include "base/sha1.h"
#include "base/string.h"
#include "base/version.h"

#include <iostream>
#include <algorithm>

#define WINTAB_TRACE(...)

namespace os {

namespace {

typedef UINT (API* WTInfoW_Func)(UINT, UINT, LPVOID);
typedef HCTX (API* WTOpenW_Func)(HWND, LPLOGCONTEXTW, BOOL);
typedef BOOL (API* WTClose_Func)(HCTX);
typedef int (API* WTPacketsGet_Func)(HCTX, int, LPVOID);
typedef BOOL (API* WTPacket_Func)(HCTX, UINT, LPVOID);
typedef BOOL (API* WTOverlap_Func)(HCTX, BOOL);
typedef int (API* WTQueueSizeGet_Func)(HCTX);
typedef BOOL (API* WTQueueSizeSet_Func)(HCTX, int);

WTInfoW_Func WTInfo;
WTOpenW_Func WTOpen;
WTClose_Func WTClose;
WTPacketsGet_Func WTPacketsGet;
WTPacket_Func WTPacket;
WTOverlap_Func WTOverlap;
WTQueueSizeGet_Func WTQueueSizeGet;
WTQueueSizeSet_Func WTQueueSizeSet;

} // anonymous namespace

WintabAPI::WintabAPI()
  : m_wintabLib(nullptr)
{
}

WintabAPI::~WintabAPI()
{
  if (!m_wintabLib)
    return;

  base::unload_dll(m_wintabLib);
  m_wintabLib = nullptr;
}

HCTX WintabAPI::open(HWND hwnd, bool moveMouse)
{
  if (!m_wintabLib && !loadWintab())
    return nullptr;

  // Log Wintab ID
  {
    UINT nchars = WTInfo(WTI_INTERFACE, IFC_WINTABID, nullptr);
    if (nchars > 0 && nchars < 1024) {
      // Some buggy wintab implementations may not report the right
      // string size in the WTInfo call above (eg.: the Genius EasyPen
      // i405X wintab). When this happens, the WTInfo call for getting
      // the tablet id will get only a part of the string, therefore
      // without the null terminating character. This will lead to a
      // buffer overrun and may just crash instantly, or cause a heap
      // corruption that will lead to a crash latter. A quick
      // workaround to this kind of wintab misinformation is to
      // oversize the buffer to guarantee that for the most common
      // string lenghts it will be enough.
      std::vector<WCHAR> buf(std::max<UINT>(128, nchars+1), 0);
      WTInfo(WTI_INTERFACE, IFC_WINTABID, &buf[0]);
      LOG("PEN: Wintab ID \"%s\"\n", base::to_utf8(&buf[0]).c_str());
    }
  }

  // Log Wintab version for debugging purposes
  {
    WORD specVer = 0;
    WORD implVer = 0;
    UINT options = 0;
    WTInfo(WTI_INTERFACE, IFC_SPECVERSION, &specVer);
    WTInfo(WTI_INTERFACE, IFC_IMPLVERSION, &implVer);
    WTInfo(WTI_INTERFACE, IFC_CTXOPTIONS, &options);
    LOG("PEN: Wintab spec v%d.%d impl v%d.%d options 0x%x\n",
        (specVer & 0xff00) >> 8, (specVer & 0xff),
        (implVer & 0xff00) >> 8, (implVer & 0xff), options);
  }

  LOGCONTEXTW logctx;
  memset(&logctx, 0, sizeof(LOGCONTEXTW));
  UINT infoRes = WTInfo(WTI_DEFSYSCTX, 0, &logctx);

  if (moveMouse) {
    // Move system cursor position, you can use packets to get
    // pressure information and cursor type, and pointer movement from
    // mouse messages.
    logctx.lcOptions |= CXO_SYSTEM;
  }
  else {
    // In this case you can process packets directly converting then
    // to events (system mouse movement messages will not be
    // generated).
    logctx.lcOptions &= ~CXO_SYSTEM;
  }

#if 1 // We shouldn't bypass WTOpen() if the return value from
      // WTInfo() isn't the expected one, WTOpen() should just fail
      // anyway.
  if (infoRes != sizeof(LOGCONTEXTW)) {
    LOG(ERROR,
        "PEN: Invalid size of WTInfo:\n"
        "     Expected context size: %d\n"
        "     Actual context size: %d\n",
        sizeof(LOGCONTEXTW), infoRes);
  }
#endif

  LOG("PEN: Context options=%d pktRate=%d in=%d,%d,%d,%d out=%d,%d,%d,%d sys=%d,%d,%d,%d\n",
      logctx.lcOptions, logctx.lcPktRate,
      logctx.lcInOrgX, logctx.lcInOrgY, logctx.lcInExtX, logctx.lcInExtY,
      logctx.lcOutOrgX, logctx.lcOutOrgY, logctx.lcOutExtX, logctx.lcOutExtY,
      logctx.lcSysOrgX, logctx.lcSysOrgY, logctx.lcSysExtX, logctx.lcSysExtY);

  logctx.lcOptions |=
    CXO_MESSAGES |
    CXO_CSRMESSAGES;
  logctx.lcPktData = PACKETDATA;
  logctx.lcPktMode = PACKETMODE;
  logctx.lcMoveMask = PACKETDATA;
  m_outBounds = gfx::Rect(logctx.lcOutOrgX, logctx.lcOutOrgY, logctx.lcOutExtX, logctx.lcOutExtY);

  AXIS pressure;
  infoRes = WTInfo(WTI_DEVICES, DVC_NPRESSURE, &pressure);
  if (infoRes >= sizeof(AXIS)) {
    m_minPressure = pressure.axMin;
    m_maxPressure = pressure.axMax;
    LOG("PEN: Min/max pressure values [%d,%d]\n", pressure.axMin, pressure.axMax);
  }
  else {
    m_minPressure = 0;
    m_maxPressure = 0;
    LOG("PEN: pressure info size %d (expected %d)", infoRes, sizeof(AXIS));
  }

  LOG("PEN: Opening context, options 0x%x\n", logctx.lcOptions);
  HCTX ctx = WTOpen(hwnd, &logctx, TRUE);
  if (!ctx) {
    LOG("PEN: Error attaching pen to display\n");
    return nullptr;
  }

  // Make the queue bigger as recommended by Wacom docs:
  //
  //   "To prevent your queue from overflowing, increase the size of
  //    your context's queue with the WTQueueSizeSet() function to a
  //    value between 32 and 128. Memory for packet queues is a limited
  //    resource, so be sure check that your call to WTQueueSizeSet()
  //    succeeds. If WTQueueSizeSet() fails, request a smaller queue
  //    size."
  //
  // https://developer-docs.wacom.com/display/DevDocs/Window+Developers+FAQ
  int q = WTQueueSizeGet(ctx);
  LOG("PEN: Original queue size=%d\n", q);
  if (q < 128) {
    for (int r=128; r>=q; r-=8) {
      if (WTQueueSizeSet(ctx, r))
        break;
    }
  }
  m_queueSize = q = WTQueueSizeGet(ctx);
  LOG("PEN: New queue size=%d\n", q);

  LOG("PEN: Pen attached to display, new context %p\n", ctx);
  return ctx;
}

void WintabAPI::close(HCTX ctx)
{
  LOG("PEN: Closing context %p\n", ctx);
  if (ctx) {
    ASSERT(m_wintabLib);
    LOG("PEN: Pen detached from window\n");
    WTClose(ctx);
  }
}

void WintabAPI::overlap(HCTX ctx, BOOL state)
{
  WTOverlap(ctx, state);
}

bool WintabAPI::packet(HCTX ctx, UINT serial, LPVOID packet)
{
  return (WTPacket(ctx, serial, packet) ? true: false);
}

int WintabAPI::packets(HCTX ctx, int maxPackets, LPVOID packets)
{
  return WTPacketsGet(ctx, maxPackets, packets);
}

void WintabAPI::mapCursorButton(const int cursor,
                                const int logicalButton,
                                const int relativeButton,
                                Event::Type& evType,
                                Event::MouseButton& mouseButton)
{
  mouseButton = Event::NoneButton;
  switch (relativeButton) {
    case TBN_DOWN:
      evType = Event::MouseDown;
      break;
    case TBN_UP:
      evType = Event::MouseUp;
      break;
    case TBN_NONE:
    default:
      evType = Event::MouseMove;
      break;
  }

  // Invalid logical button
  if (logicalButton < 0 || logicalButton >= 32) {
    WINTAB_TRACE("PEN: INVALID LOGICAL BUTTON\n");
    return;
  }

  // Get "logical button" -> "button action code" mapping so we can
  // know for what specific mouse button we should generate an event
  // (or maybe if it's a double-click).
  BYTE map[32];
  WTInfo(WTI_CURSORS + cursor, CSR_SYSBTNMAP, &map);

  switch (map[logicalButton]) {

    case SBN_LDBLCLICK:
      evType = Event::MouseDoubleClick;
    case SBN_LCLICK:
    case SBN_LDRAG:
      mouseButton = Event::LeftButton;
      break;

    case SBN_RDBLCLICK:
      evType = Event::MouseDoubleClick;
    case SBN_RCLICK:
    case SBN_RDRAG:
      mouseButton = Event::RightButton;
      break;

    case SBN_MDBLCLICK:
      evType = Event::MouseDoubleClick;
    case SBN_MCLICK:
    case SBN_MDRAG:
      mouseButton = Event::MiddleButton;
      break;
  }

  WINTAB_TRACE(
    "  PEN: Button map logicalButton=%d action=%d -> evType=%s mouseButton=%d\n",
    logicalButton,
    map[logicalButton],
    (evType == Event::None ? "-":
     evType == Event::MouseMove ? "move":
     evType == Event::MouseDown ? "DOWN":
     evType == Event::MouseUp ? "UP":
     evType == Event::MouseDoubleClick ? "DOUBLE-CLICK": "unknown"),
    (int)mouseButton);
}

bool WintabAPI::loadWintab()
{
  ASSERT(!m_wintabLib);

  m_wintabLib = base::load_dll("wintab32.dll");
  if (!m_wintabLib) {
    LOG(ERROR, "PEN: wintab32.dll is not present\n");
    return false;
  }

  if (!checkDll()) {
    base::unload_dll(m_wintabLib);
    m_wintabLib = nullptr;
    return false;
  }

  WTInfo = base::get_dll_proc<WTInfoW_Func>(m_wintabLib, "WTInfoW");
  WTOpen = base::get_dll_proc<WTOpenW_Func>(m_wintabLib, "WTOpenW");
  WTClose = base::get_dll_proc<WTClose_Func>(m_wintabLib, "WTClose");
  WTPacketsGet = base::get_dll_proc<WTPacketsGet_Func>(m_wintabLib, "WTPacketsGet");
  WTPacket = base::get_dll_proc<WTPacket_Func>(m_wintabLib, "WTPacket");
  WTOverlap = base::get_dll_proc<WTOverlap_Func>(m_wintabLib, "WTOverlap");
  WTQueueSizeGet = base::get_dll_proc<WTQueueSizeGet_Func>(m_wintabLib, "WTQueueSizeGet");
  WTQueueSizeSet = base::get_dll_proc<WTQueueSizeSet_Func>(m_wintabLib, "WTQueueSizeSet");
  if (!WTInfo || !WTOpen || !WTClose || !WTPacket ||
      !WTQueueSizeGet || !WTQueueSizeSet) {
    LOG(ERROR, "PEN: wintab32.dll does not contain all required functions\n");
    return false;
  }

  LOG("PEN: Wintab library loaded\n");
  return true;
}

bool WintabAPI::checkDll()
{
  ASSERT(m_wintabLib);

  std::string fn = base::get_dll_filename(m_wintabLib);
  if (!base::is_file(fn))
    return false;

  std::string checksum = base::convert_to<std::string>(base::Sha1::calculateFromFile(fn));
  base::Version ver = base::get_file_version(fn);
  LOG("PEN: <%s> v%s, sha1 <%s>\n", fn.c_str(), ver.str().c_str(), checksum.c_str());

  // Ugly hack to bypass the buggy WALTOP International Corp .dll that
  // hangs Aseprite completely when we call its WTInfo function.
  if (checksum == "a3ba0d9c0f5d8b9f4070981b243a80579f8be105")
    return false;

  return true;
}

} // namespace os
<commit_msg>[win] Don't ask for Wintab information if we are not logging info<commit_after>// LAF OS Library
// Copyright (C) 2020  Igara Studio S.A.
// Copyright (C) 2016-2017  David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.

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

#include "os/win/wintab.h"

#include "base/convert_to.h"
#include "base/debug.h"
#include "base/fs.h"
#include "base/log.h"
#include "base/sha1.h"
#include "base/string.h"
#include "base/version.h"

#include <iostream>
#include <algorithm>

#define WINTAB_TRACE(...)

namespace os {

namespace {

typedef UINT (API* WTInfoW_Func)(UINT, UINT, LPVOID);
typedef HCTX (API* WTOpenW_Func)(HWND, LPLOGCONTEXTW, BOOL);
typedef BOOL (API* WTClose_Func)(HCTX);
typedef int (API* WTPacketsGet_Func)(HCTX, int, LPVOID);
typedef BOOL (API* WTPacket_Func)(HCTX, UINT, LPVOID);
typedef BOOL (API* WTOverlap_Func)(HCTX, BOOL);
typedef int (API* WTQueueSizeGet_Func)(HCTX);
typedef BOOL (API* WTQueueSizeSet_Func)(HCTX, int);

WTInfoW_Func WTInfo;
WTOpenW_Func WTOpen;
WTClose_Func WTClose;
WTPacketsGet_Func WTPacketsGet;
WTPacket_Func WTPacket;
WTOverlap_Func WTOverlap;
WTQueueSizeGet_Func WTQueueSizeGet;
WTQueueSizeSet_Func WTQueueSizeSet;

} // anonymous namespace

WintabAPI::WintabAPI()
  : m_wintabLib(nullptr)
{
}

WintabAPI::~WintabAPI()
{
  if (!m_wintabLib)
    return;

  base::unload_dll(m_wintabLib);
  m_wintabLib = nullptr;
}

HCTX WintabAPI::open(HWND hwnd, bool moveMouse)
{
  if (!m_wintabLib && !loadWintab())
    return nullptr;

  // Only on INFO or VERBOSE modes for debugging purposes
  if (base::get_log_level() >= INFO) {
    // Log Wintab ID
    UINT nchars = WTInfo(WTI_INTERFACE, IFC_WINTABID, nullptr);
    if (nchars > 0 && nchars < 1024) {
      // Some buggy wintab implementations may not report the right
      // string size in the WTInfo call above (eg.: the Genius EasyPen
      // i405X wintab). When this happens, the WTInfo call for getting
      // the tablet id will get only a part of the string, therefore
      // without the null terminating character. This will lead to a
      // buffer overrun and may just crash instantly, or cause a heap
      // corruption that will lead to a crash latter. A quick
      // workaround to this kind of wintab misinformation is to
      // oversize the buffer to guarantee that for the most common
      // string lenghts it will be enough.
      std::vector<WCHAR> buf(std::max<UINT>(128, nchars+1), 0);
      WTInfo(WTI_INTERFACE, IFC_WINTABID, &buf[0]);
      LOG("PEN: Wintab ID \"%s\"\n", base::to_utf8(&buf[0]).c_str());
    }

    // Log Wintab version
    WORD specVer = 0;
    WORD implVer = 0;
    UINT options = 0;
    WTInfo(WTI_INTERFACE, IFC_SPECVERSION, &specVer);
    WTInfo(WTI_INTERFACE, IFC_IMPLVERSION, &implVer);
    WTInfo(WTI_INTERFACE, IFC_CTXOPTIONS, &options);
    LOG("PEN: Wintab spec v%d.%d impl v%d.%d options 0x%x\n",
        (specVer & 0xff00) >> 8, (specVer & 0xff),
        (implVer & 0xff00) >> 8, (implVer & 0xff), options);
  }

  LOGCONTEXTW logctx;
  memset(&logctx, 0, sizeof(LOGCONTEXTW));
  UINT infoRes = WTInfo(WTI_DEFSYSCTX, 0, &logctx);

  if (moveMouse) {
    // Move system cursor position, you can use packets to get
    // pressure information and cursor type, and pointer movement from
    // mouse messages.
    logctx.lcOptions |= CXO_SYSTEM;
  }
  else {
    // In this case you can process packets directly converting then
    // to events (system mouse movement messages will not be
    // generated).
    logctx.lcOptions &= ~CXO_SYSTEM;
  }

#if 1 // We shouldn't bypass WTOpen() if the return value from
      // WTInfo() isn't the expected one, WTOpen() should just fail
      // anyway.
  if (infoRes != sizeof(LOGCONTEXTW)) {
    LOG(ERROR,
        "PEN: Invalid size of WTInfo:\n"
        "     Expected context size: %d\n"
        "     Actual context size: %d\n",
        sizeof(LOGCONTEXTW), infoRes);
  }
#endif

  LOG("PEN: Context options=%d pktRate=%d in=%d,%d,%d,%d out=%d,%d,%d,%d sys=%d,%d,%d,%d\n",
      logctx.lcOptions, logctx.lcPktRate,
      logctx.lcInOrgX, logctx.lcInOrgY, logctx.lcInExtX, logctx.lcInExtY,
      logctx.lcOutOrgX, logctx.lcOutOrgY, logctx.lcOutExtX, logctx.lcOutExtY,
      logctx.lcSysOrgX, logctx.lcSysOrgY, logctx.lcSysExtX, logctx.lcSysExtY);

  logctx.lcOptions |=
    CXO_MESSAGES |
    CXO_CSRMESSAGES;
  logctx.lcPktData = PACKETDATA;
  logctx.lcPktMode = PACKETMODE;
  logctx.lcMoveMask = PACKETDATA;
  m_outBounds = gfx::Rect(logctx.lcOutOrgX, logctx.lcOutOrgY, logctx.lcOutExtX, logctx.lcOutExtY);

  AXIS pressure;
  infoRes = WTInfo(WTI_DEVICES, DVC_NPRESSURE, &pressure);
  if (infoRes >= sizeof(AXIS)) {
    m_minPressure = pressure.axMin;
    m_maxPressure = pressure.axMax;
    LOG("PEN: Min/max pressure values [%d,%d]\n", pressure.axMin, pressure.axMax);
  }
  else {
    m_minPressure = 0;
    m_maxPressure = 0;
    LOG("PEN: pressure info size %d (expected %d)", infoRes, sizeof(AXIS));
  }

  LOG("PEN: Opening context, options 0x%x\n", logctx.lcOptions);
  HCTX ctx = WTOpen(hwnd, &logctx, TRUE);
  if (!ctx) {
    LOG("PEN: Error attaching pen to display\n");
    return nullptr;
  }

  // Make the queue bigger as recommended by Wacom docs:
  //
  //   "To prevent your queue from overflowing, increase the size of
  //    your context's queue with the WTQueueSizeSet() function to a
  //    value between 32 and 128. Memory for packet queues is a limited
  //    resource, so be sure check that your call to WTQueueSizeSet()
  //    succeeds. If WTQueueSizeSet() fails, request a smaller queue
  //    size."
  //
  // https://developer-docs.wacom.com/display/DevDocs/Window+Developers+FAQ
  int q = WTQueueSizeGet(ctx);
  LOG("PEN: Original queue size=%d\n", q);
  if (q < 128) {
    for (int r=128; r>=q; r-=8) {
      if (WTQueueSizeSet(ctx, r))
        break;
    }
  }
  m_queueSize = q = WTQueueSizeGet(ctx);
  LOG("PEN: New queue size=%d\n", q);

  LOG("PEN: Pen attached to display, new context %p\n", ctx);
  return ctx;
}

void WintabAPI::close(HCTX ctx)
{
  LOG("PEN: Closing context %p\n", ctx);
  if (ctx) {
    ASSERT(m_wintabLib);
    LOG("PEN: Pen detached from window\n");
    WTClose(ctx);
  }
}

void WintabAPI::overlap(HCTX ctx, BOOL state)
{
  WTOverlap(ctx, state);
}

bool WintabAPI::packet(HCTX ctx, UINT serial, LPVOID packet)
{
  return (WTPacket(ctx, serial, packet) ? true: false);
}

int WintabAPI::packets(HCTX ctx, int maxPackets, LPVOID packets)
{
  return WTPacketsGet(ctx, maxPackets, packets);
}

void WintabAPI::mapCursorButton(const int cursor,
                                const int logicalButton,
                                const int relativeButton,
                                Event::Type& evType,
                                Event::MouseButton& mouseButton)
{
  mouseButton = Event::NoneButton;
  switch (relativeButton) {
    case TBN_DOWN:
      evType = Event::MouseDown;
      break;
    case TBN_UP:
      evType = Event::MouseUp;
      break;
    case TBN_NONE:
    default:
      evType = Event::MouseMove;
      break;
  }

  // Invalid logical button
  if (logicalButton < 0 || logicalButton >= 32) {
    WINTAB_TRACE("PEN: INVALID LOGICAL BUTTON\n");
    return;
  }

  // Get "logical button" -> "button action code" mapping so we can
  // know for what specific mouse button we should generate an event
  // (or maybe if it's a double-click).
  BYTE map[32];
  WTInfo(WTI_CURSORS + cursor, CSR_SYSBTNMAP, &map);

  switch (map[logicalButton]) {

    case SBN_LDBLCLICK:
      evType = Event::MouseDoubleClick;
    case SBN_LCLICK:
    case SBN_LDRAG:
      mouseButton = Event::LeftButton;
      break;

    case SBN_RDBLCLICK:
      evType = Event::MouseDoubleClick;
    case SBN_RCLICK:
    case SBN_RDRAG:
      mouseButton = Event::RightButton;
      break;

    case SBN_MDBLCLICK:
      evType = Event::MouseDoubleClick;
    case SBN_MCLICK:
    case SBN_MDRAG:
      mouseButton = Event::MiddleButton;
      break;
  }

  WINTAB_TRACE(
    "  PEN: Button map logicalButton=%d action=%d -> evType=%s mouseButton=%d\n",
    logicalButton,
    map[logicalButton],
    (evType == Event::None ? "-":
     evType == Event::MouseMove ? "move":
     evType == Event::MouseDown ? "DOWN":
     evType == Event::MouseUp ? "UP":
     evType == Event::MouseDoubleClick ? "DOUBLE-CLICK": "unknown"),
    (int)mouseButton);
}

bool WintabAPI::loadWintab()
{
  ASSERT(!m_wintabLib);

  m_wintabLib = base::load_dll("wintab32.dll");
  if (!m_wintabLib) {
    LOG(ERROR, "PEN: wintab32.dll is not present\n");
    return false;
  }

  if (!checkDll()) {
    base::unload_dll(m_wintabLib);
    m_wintabLib = nullptr;
    return false;
  }

  WTInfo = base::get_dll_proc<WTInfoW_Func>(m_wintabLib, "WTInfoW");
  WTOpen = base::get_dll_proc<WTOpenW_Func>(m_wintabLib, "WTOpenW");
  WTClose = base::get_dll_proc<WTClose_Func>(m_wintabLib, "WTClose");
  WTPacketsGet = base::get_dll_proc<WTPacketsGet_Func>(m_wintabLib, "WTPacketsGet");
  WTPacket = base::get_dll_proc<WTPacket_Func>(m_wintabLib, "WTPacket");
  WTOverlap = base::get_dll_proc<WTOverlap_Func>(m_wintabLib, "WTOverlap");
  WTQueueSizeGet = base::get_dll_proc<WTQueueSizeGet_Func>(m_wintabLib, "WTQueueSizeGet");
  WTQueueSizeSet = base::get_dll_proc<WTQueueSizeSet_Func>(m_wintabLib, "WTQueueSizeSet");
  if (!WTInfo || !WTOpen || !WTClose || !WTPacket ||
      !WTQueueSizeGet || !WTQueueSizeSet) {
    LOG(ERROR, "PEN: wintab32.dll does not contain all required functions\n");
    return false;
  }

  LOG("PEN: Wintab library loaded\n");
  return true;
}

bool WintabAPI::checkDll()
{
  ASSERT(m_wintabLib);

  std::string fn = base::get_dll_filename(m_wintabLib);
  if (!base::is_file(fn))
    return false;

  std::string checksum = base::convert_to<std::string>(base::Sha1::calculateFromFile(fn));
  base::Version ver = base::get_file_version(fn);
  LOG("PEN: <%s> v%s, sha1 <%s>\n", fn.c_str(), ver.str().c_str(), checksum.c_str());

  // Ugly hack to bypass the buggy WALTOP International Corp .dll that
  // hangs Aseprite completely when we call its WTInfo function.
  if (checksum == "a3ba0d9c0f5d8b9f4070981b243a80579f8be105")
    return false;

  return true;
}

} // namespace os
<|endoftext|>


<commit_before>/**
 * Instructions:
 * Implement the code, and write comments described in the
 * //TODO:
 * formatted comments.
 * //NOTE:
 * comments clarify parts of the given code.
 **/

//TODO: Insert your header
//NOTE: Your header no longer needs to include inputs and outputs

#include <cassert>
#include <string>

using namespace std;

/**
 * Function Declarations
 **/

//IGNORE: You can ignore these functions
void test();
void run_loop();
void run();
void  test_unit_to_s();
void  test_to_c();
void  test_from_c();
void  test_convert();

//TODO: Write header comments for each of the following function declarations.
string unit_to_s(char unit);
double to_c(char units, double temp);
double from_c(char units, double temp);
double convert(char in, char out, double temp);

/**
 * Main Function
 **/

//IGNORE: You may safely ignore the main function.
int main() {
  char t;

  cout << "Enter [t] to test, or any other character to run." << endl;
  cin >> t;
  if(t == 't') {
    test();
  } else {
    run_loop();
  }
}

/**
 * Function Definitions
 **/
//IGNORE: You may ignore this tester.
void test() {
  test_unit_to_s();
  test_to_c();
  test_from_c();
  test_convert();
}

//IGNORE: You may ignore this function.
void run_loop() {
  char again = 'y';

  while(again == 'y' || again == 'Y') {
    run();
    cout << "Calculate again?" << endl;
    cin >> again;
  }
}
//NOTE: You do not have to edit this function, but you should use it as a guide
//      to understand how to implement the functions.
void run() {
  // Variable Declarations
  char units_in, units_out;
  double temp;

  // Input
  cout << "Enter the units you'd like to convert from:" << endl;
  cin >> units_in;
  cout << "Enter the units you'd like to convert to:" << endl;
  cin >> units_out;
  cout << "Enter the temperature in " << unit_to_s(units_in) << ":" << endl;
  cin >> temp;

  // Output
  cout << temp << " "
       << unit_to_s(units_in) << " is "
       << unit_to_s(units_out) << " "
       << convert(units_in, units_out, temp) << "." << endl;
}

//TODO: Implement each of the functions below
//NOTE: You should be able to copy and modify much of your code
//      from the previous homework.
//TODO: Test each of the functions below
//NOTE: make sure you test each possible case
void  test_unit_to_s() {
  assert(unit_to_s('c') == "celcius");
}
string unit_to_s(char unit) {
  return "celcius";
}

void  test_to_c() {
  assert(to_c('f',32) == 0);
}
double to_c(char units, double temp) {
  return 0;
}

void  test_from_c() {
  assert(from_c('f',0) == 32);
}
double from_c(char units, double temp){
  return 32;
}

void  test_convert() {
  assert(convert('f','c',32) == 0);
}
double convert(char in, char out, double temp) {
  return 0;
}
<commit_msg>modified instructions<commit_after>/**
 * Instructions:
 * Implement the code, and write comments described in the
 * //TODO:
 * formatted comments.
 *
 * //NOTE:
 * comments clarify parts of the given code.
 *
 * //IGNORE:
 * comments mark code which can be safely ignored.
 **/

//TODO: Insert your header
//NOTE: Your header no longer needs to include inputs and outputs

#include <iostream>
#include <cassert>
#include <string>

using namespace std;

/**
 * Function Declarations
 **/

//IGNORE: You can ignore these functions
void test();
void run_loop();
void run();
void  test_unit_to_s();
void  test_to_c();
void  test_from_c();
void  test_convert();

//TODO: Write header comments for each of the following function declarations.
string unit_to_s(char unit);
double to_c(char units, double temp);
double from_c(char units, double temp);
double convert(char in, char out, double temp);

/**
 * Main Function
 **/

//IGNORE: You may safely ignore the main function.
int main() {
  char t;

  cout << "Enter [t] to test, or any other character to run." << endl;
  cin >> t;
  if(t == 't') {
    test();
  } else {
    run_loop();
  }
}

/**
 * Function Definitions
 **/
//IGNORE: You may ignore this tester.
void test() {
  test_unit_to_s();
  test_to_c();
  test_from_c();
  test_convert();
}

//IGNORE: You may ignore this function.
void run_loop() {
  char again = 'y';

  while(again == 'y' || again == 'Y') {
    run();
    cout << "Calculate again?" << endl;
    cin >> again;
  }
}
//NOTE: You do not have to edit this function, but you should use it as a guide
//      to understand how to implement the functions.
void run() {
  // Variable Declarations
  char units_in, units_out;
  double temp;

  // Input
  cout << "Enter the units you'd like to convert from:" << endl;
  cin >> units_in;
  cout << "Enter the units you'd like to convert to:" << endl;
  cin >> units_out;
  cout << "Enter the temperature in " << unit_to_s(units_in) << ":" << endl;
  cin >> temp;

  // Output
  cout << temp << " "
       << unit_to_s(units_in) << " is "
       << unit_to_s(units_out) << " "
       << convert(units_in, units_out, temp) << "." << endl;
}

//TODO: Implement each of the functions below
//NOTE: You should be able to copy and modify much of your code
//      from the previous homework.
//TODO: Test each of the functions below
//NOTE: make sure you test each possible case
void  test_unit_to_s() {
  assert(unit_to_s('c') == "celcius");
}
string unit_to_s(char unit) {
  return "celcius";
}

void  test_to_c() {
  assert(to_c('f',32) == 0);
}
double to_c(char units, double temp) {
  return 0;
}

void  test_from_c() {
  assert(from_c('f',0) == 32);
}
double from_c(char units, double temp){
  return 32;
}

void  test_convert() {
  assert(convert('f','c',32) == 0);
}
double convert(char in, char out, double temp) {
  return 0;
}
<|endoftext|>