<commit_before>/*
 *  FileSystem.cc
 *  Apto
 *
 *  Created by David on 12/7/05.
 *  Copyright 2005-2011 David Michael Bryson. All rights reserved.
 *  http://programerror.com/software/apto
 *
 *  Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
 *  following conditions are met:
 *
 *  1.  Redistributions of source code must retain the above copyright notice, this list of conditions and the
 *      following disclaimer.
 *  2.  Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
 *      following disclaimer in the documentation and/or other materials provided with the distribution.
 *  3.  Neither the name of David Michael Bryson, nor the names of contributors may be used to endorse or promote
 *      products derived from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY DAVID MICHAEL BRYSON 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 DAVID MICHAEL BRYSON 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.
 *
 *  Authors: David M. Bryson <david@programerror.com>
 *
 */

#include "apto/core/FileSystem.h"

#include "apto/platform/Platform.h"
#include "apto/platform/FileSystem.h"

#include <cerrno>
#include <sys/stat.h>
#include <cstdio>
#include <fstream>

// mkdir undefined in ms windows
#if APTO_PLATFORM(WINDOWS)
# include <direct.h>
# ifndef ACCESSPERMS
#  define ACCESSPERMS 0
# endif
# ifndef mkdir
#  define mkdir(path, ignored_mode) _mkdir(path)
# endif
# ifndef mode_t
#  define mode_t unsigned int
# endif
#endif

#if APTO_PLATFORM(WINDOWS)
# include <direct.h>
#else
# include <unistd.h>
#endif

namespace Apto {
  namespace FileSystem {

    String GetCWD()
    {
      String cwd_str;

      char* dirbuf = new char[MAXIMUM_DIRECTORY_LENGTH];
#if APTO_PLATFORM(WINDOWS)
      char* cwd = _getcwd(dirbuf, MAXIMUM_DIRECTORY_LENGTH);
#else
      char* cwd = getcwd(dirbuf, MAXIMUM_DIRECTORY_LENGTH);
#endif
      if (cwd != NULL) cwd_str = cwd;
      delete [] dirbuf;

      return cwd_str;
    }

    String GetAbsolutePath(const String& path, const String& working_dir)
    {
      if (path.GetSize() == 0) return working_dir;

#if APTO_PLATFORM(WINDOWS)
      if (!(path.IsLetter(0) && path[1] == ':' && path[2] =='\\')) return PathAppend(working_dir, path);
#else
      if (path[0] != '/' && path[0] != '\\') return PathAppend(working_dir, path);
#endif

      return path;
    }

    bool IsDir(const String& path)
    {
      struct stat st;
      if (stat(path, &st) == 0 && st.st_mode & S_IFDIR) return true;
      return false;
    }

    bool IsFile(const String& path)
    {
      struct stat st;
      if (stat(path, &st) == 0 && st.st_mode & S_IFREG) return true;
      return false;
    }

    bool CpDir(const String& src, const String& dest)
    {
      if (!IsDir(src)) return false;

      MkDir(dest);

      // Collect entries in directory
      Array<String, Smart> direntries;
      ReadDir(src, direntries);

      // Process all entries, removing them as appropriate
      for (int i = 0; i < direntries.GetSize(); i++) {
        // Do not copy current or parent directory entries
        if (direntries[i] == "." || direntries[i] == "..") continue;

        Apto::String src_path = PathAppend(src, direntries[i]);
        Apto::String dest_path = PathAppend(dest, direntries[i]);

        struct stat st;
        if (stat(src_path, &st) != 0) continue;

        if (st.st_mode & S_IFDIR) {
          // Recursively copy subdirectory
          if (!CpDir(src_path, dest_path)) return false;
        } else if (st.st_mode & S_IFREG) {
          // Cop regular file
          if (!CpFile(src_path, dest_path)) return false;
        }
      }

      return true;
    }

    bool MkDir(const String& dirname)
    {
      FILE* fp = 0;

#if APTO_PLATFORM(WINDOWS)
      if (fopen_s(&fp, dirname, "r") == 0) {
#else
      fp = fopen(dirname, "r");
      if (fp == 0) {
#endif
        if (errno == ENOENT) {
          // not found, creating...
          if (mkdir(dirname, (S_IRWXU | S_IRWXG | S_IRWXO))) return false;

          return true;
        }

        return false;
      }
      fclose(fp);

      // found
      return true;
    }

    bool RmDir(const String& dirname, bool recursive)
    {
      if (!IsDir(dirname)) return false;

      if (recursive) {
        // Collect entries in directory
        Array<String, Smart> direntries;
        ReadDir(dirname, direntries);

        // Process all entries, removing them as appropriate
        for (int i = 0; i < direntries.GetSize(); i++) {
          // Do not remove current or parent directory entries
          if (direntries[i] == "." || direntries[i] == "..") continue;

          Apto::String path = PathAppend(dirname, direntries[i]);

          struct stat st;
          if (stat(path, &st) != 0) continue;

          if (st.st_mode & S_IFDIR) {
            // Recursively remove subdirectory
            if (!RmDir(path)) return false;
          } else {
            // Remove regular (or other) file
            if (remove(path) != 0) return false;
          }
        }
      }

      // Attempt to remove the directory itself
#if APTO_PLATFORM(WINDOWS)
      return (_rmdir(dirname) == 0);
#else
      return (rmdir(dirname) == 0);
#endif
    }

    bool ReadDir(const String& path, Array<String, Smart>& entries)
    {
      DIR* dp;
      struct dirent* dirp;
      if ((dp = opendir(path)) == NULL) return false;

      while ((dirp = readdir(dp)) != NULL) entries.Push(dirp->d_name);

      closedir(dp);
      return true;
    }

    bool CpFile(const String& src, const String& dest)
    {
      std::ifstream ifs(src, std::ios::binary);
      std::ofstream ofs(dest, std::ios::binary);

      if (!ifs.is_open() || !ofs.is_open()) return false;

      ofs << ifs.rdbuf();

      ifs.close();
      ofs.close();

      return true;
    }

    bool RmFile(const String& path)
    {
      return (remove(path) == 0);
    }

  };
};
<commit_msg>Fix create folder support on windows.<commit_after>/*
 *  FileSystem.cc
 *  Apto
 *
 *  Created by David on 12/7/05.
 *  Copyright 2005-2011 David Michael Bryson. All rights reserved.
 *  http://programerror.com/software/apto
 *
 *  Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
 *  following conditions are met:
 *
 *  1.  Redistributions of source code must retain the above copyright notice, this list of conditions and the
 *      following disclaimer.
 *  2.  Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
 *      following disclaimer in the documentation and/or other materials provided with the distribution.
 *  3.  Neither the name of David Michael Bryson, nor the names of contributors may be used to endorse or promote
 *      products derived from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY DAVID MICHAEL BRYSON 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 DAVID MICHAEL BRYSON 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.
 *
 *  Authors: David M. Bryson <david@programerror.com>
 *
 */

#include "apto/core/FileSystem.h"

#include "apto/platform/Platform.h"
#include "apto/platform/FileSystem.h"

#include <cerrno>
#include <sys/stat.h>
#include <cstdio>
#include <fstream>

// mkdir undefined in ms windows
#if APTO_PLATFORM(WINDOWS)
# include <direct.h>
# ifndef ACCESSPERMS
#  define ACCESSPERMS 0
# endif
# ifndef mkdir
#  define mkdir(path, ignored_mode) _mkdir(path)
# endif
# ifndef mode_t
#  define mode_t unsigned int
# endif
#endif

#if APTO_PLATFORM(WINDOWS)
# include <direct.h>
#else
# include <unistd.h>
#endif

namespace Apto {
  namespace FileSystem {

    String GetCWD()
    {
      String cwd_str;

      char* dirbuf = new char[MAXIMUM_DIRECTORY_LENGTH];
#if APTO_PLATFORM(WINDOWS)
      char* cwd = _getcwd(dirbuf, MAXIMUM_DIRECTORY_LENGTH);
#else
      char* cwd = getcwd(dirbuf, MAXIMUM_DIRECTORY_LENGTH);
#endif
      if (cwd != NULL) cwd_str = cwd;
      delete [] dirbuf;

      return cwd_str;
    }

    String GetAbsolutePath(const String& path, const String& working_dir)
    {
      if (path.GetSize() == 0) return working_dir;

#if APTO_PLATFORM(WINDOWS)
      if (!(path.IsLetter(0) && path[1] == ':' && path[2] =='\\')) return PathAppend(working_dir, path);
#else
      if (path[0] != '/' && path[0] != '\\') return PathAppend(working_dir, path);
#endif

      return path;
    }

    bool IsDir(const String& path)
    {
      struct stat st;
      if (stat(path, &st) == 0 && st.st_mode & S_IFDIR) return true;
      return false;
    }

    bool IsFile(const String& path)
    {
      struct stat st;
      if (stat(path, &st) == 0 && st.st_mode & S_IFREG) return true;
      return false;
    }

    bool CpDir(const String& src, const String& dest)
    {
      if (!IsDir(src)) return false;

      MkDir(dest);

      // Collect entries in directory
      Array<String, Smart> direntries;
      ReadDir(src, direntries);

      // Process all entries, removing them as appropriate
      for (int i = 0; i < direntries.GetSize(); i++) {
        // Do not copy current or parent directory entries
        if (direntries[i] == "." || direntries[i] == "..") continue;

        Apto::String src_path = PathAppend(src, direntries[i]);
        Apto::String dest_path = PathAppend(dest, direntries[i]);

        struct stat st;
        if (stat(src_path, &st) != 0) continue;

        if (st.st_mode & S_IFDIR) {
          // Recursively copy subdirectory
          if (!CpDir(src_path, dest_path)) return false;
        } else if (st.st_mode & S_IFREG) {
          // Cop regular file
          if (!CpFile(src_path, dest_path)) return false;
        }
      }

      return true;
    }

    bool MkDir(const String& dirname)
    {
      FILE* fp = 0;

#if APTO_PLATFORM(WINDOWS)
      fopen_s(&fp, dirname, "r");
      if (fp == 0) {
#else
      fp = fopen(dirname, "r");
      if (fp == 0) {
#endif
        if (errno == ENOENT) {
          // not found, creating...
          if (mkdir(dirname, (S_IRWXU | S_IRWXG | S_IRWXO))) return false;

          return true;
        }

        return false;
      }
      fclose(fp);

      // found
      return true;
    }

    bool RmDir(const String& dirname, bool recursive)
    {
      if (!IsDir(dirname)) return false;

      if (recursive) {
        // Collect entries in directory
        Array<String, Smart> direntries;
        ReadDir(dirname, direntries);

        // Process all entries, removing them as appropriate
        for (int i = 0; i < direntries.GetSize(); i++) {
          // Do not remove current or parent directory entries
          if (direntries[i] == "." || direntries[i] == "..") continue;

          Apto::String path = PathAppend(dirname, direntries[i]);

          struct stat st;
          if (stat(path, &st) != 0) continue;

          if (st.st_mode & S_IFDIR) {
            // Recursively remove subdirectory
            if (!RmDir(path)) return false;
          } else {
            // Remove regular (or other) file
            if (remove(path) != 0) return false;
          }
        }
      }

      // Attempt to remove the directory itself
#if APTO_PLATFORM(WINDOWS)
      return (_rmdir(dirname) == 0);
#else
      return (rmdir(dirname) == 0);
#endif
    }

    bool ReadDir(const String& path, Array<String, Smart>& entries)
    {
      DIR* dp;
      struct dirent* dirp;
      if ((dp = opendir(path)) == NULL) return false;

      while ((dirp = readdir(dp)) != NULL) entries.Push(dirp->d_name);

      closedir(dp);
      return true;
    }

    bool CpFile(const String& src, const String& dest)
    {
      std::ifstream ifs(src, std::ios::binary);
      std::ofstream ofs(dest, std::ios::binary);

      if (!ifs.is_open() || !ofs.is_open()) return false;

      ofs << ifs.rdbuf();

      ifs.close();
      ofs.close();

      return true;
    }

    bool RmFile(const String& path)
    {
      return (remove(path) == 0);
    }

  };
};
<|endoftext|>


<commit_before>//==================================================================
// ָOfferԹپͱ⡷
// ߣκ
//==================================================================

// 14
// ĿһΪnӣӼmΣmnn>1m1
// ÿεӵĳȼΪk[0]k[1]k[m]k[0]*k[1]**k[m]ܵ
// Ƕ٣統ӵĳ8ʱǰɳȷֱΪ233Σ
// ʱõĳ˻18

#include <iostream>
#include <cmath>

int maxProductAfterCutting_solution1(int length)
{
    if(length < 2)
        return 0;
    if(length == 2)
        return 1;
    if(length == 3)
        return 2;

    int* products = new int[length + 1];
    products[0] = 0;
    products[1] = 1;
    products[2] = 2;
    products[3] = 3;

    int max = 0;
    for(int i = 4; i <= length; ++i)
    {
        max = 0;
        for(int j = 1; j <= i / 2; ++j)
        {
            int product = products[j] * products[i - j];
            if(max < product)
                max = product;

            products[i] = max;
        }
    }

    max = products[length];
    delete[] products;

    return max;
}

int maxProductAfterCutting_solution2(int length)
{
    if(length < 2)
        return 0;
    if(length == 2)
        return 1;
    if(length == 3)
        return 2;

    int timesOf3 = length / 3;
    if((length - timesOf3 * 3) % 2 == 1)
        timesOf3 -= 1;

    int timesOf2 = (length - timesOf3 * 3) / 2;

    return (int) (pow(3, timesOf3)) * (int) (pow(2, timesOf2));
}

// ====================Դ====================
void test(const char* testName, int length, int expected)
{
    int result1 = maxProductAfterCutting_solution1(length);
    if(result1 == expected)
        std::cout << "Solution1 for " << testName << " passed." << std::endl;
    else
        std::cout << "Solution1 for " << testName << " FAILED." << std::endl;

    int result2 = maxProductAfterCutting_solution2(length);
    if(result2 == expected)
        std::cout << "Solution2 for " << testName << " passed." << std::endl;
    else
        std::cout << "Solution2 for " << testName << " FAILED." << std::endl;
}

void test1()
{
    int length = 1;
    int expected = 0;
    test("test1", length, expected);
}

void test2()
{
    int length = 2;
    int expected = 1;
    test("test2", length, expected);
}

void test3()
{
    int length = 3;
    int expected = 2;
    test("test3", length, expected);
}

void test4()
{
    int length = 4;
    int expected = 4;
    test("test4", length, expected);
}

void test5()
{
    int length = 5;
    int expected = 6;
    test("test5", length, expected);
}

void test6()
{
    int length = 6;
    int expected = 9;
    test("test6", length, expected);
}

void test7()
{
    int length = 7;
    int expected = 12;
    test("test7", length, expected);
}

void test8()
{
    int length = 8;
    int expected = 18;
    test("test8", length, expected);
}

void test9()
{
    int length = 9;
    int expected = 27;
    test("test9", length, expected);
}

void test10()
{
    int length = 10;
    int expected = 36;
    test("test10", length, expected);
}

void test11()
{
    int length = 50;
    int expected = 86093442;
    test("test11", length, expected);
}

int main(int agrc, char* argv[])
{
    test1();
    test2();
    test3();
    test4();
    test5();
    test6();
    test7();
    test8();
    test9();
    test10();
    test11();
}
<commit_msg>Changes for question 14<commit_after>//==================================================================
// ָOfferԹپͱ⡷
// ߣκ
//==================================================================

// 14
// ĿһΪnӣӼmΣmnn>1m1
// ÿεӵĳȼΪk[0]k[1]k[m]k[0]*k[1]**k[m]ܵ
// Ƕ٣統ӵĳ8ʱǰɳȷֱΪ233Σ
// ʱõĳ˻18

#include <iostream>
#include <cmath>

// ====================̬滮====================
int maxProductAfterCutting_solution1(int length)
{
    if(length < 2)
        return 0;
    if(length == 2)
        return 1;
    if(length == 3)
        return 2;

    int* products = new int[length + 1];
    products[0] = 0;
    products[1] = 1;
    products[2] = 2;
    products[3] = 3;

    int max = 0;
    for(int i = 4; i <= length; ++i)
    {
        max = 0;
        for(int j = 1; j <= i / 2; ++j)
        {
            int product = products[j] * products[i - j];
            if(max < product)
                max = product;

            products[i] = max;
        }
    }

    max = products[length];
    delete[] products;

    return max;
}

// ====================̰㷨====================
int maxProductAfterCutting_solution2(int length)
{
    if(length < 2)
        return 0;
    if(length == 2)
        return 1;
    if(length == 3)
        return 2;

    // ܶؼȥΪ3Ӷ
    int timesOf3 = length / 3;

    // ʣµĳΪ4ʱ򣬲ټȥΪ3ӶΡ
    // ʱõķǰӼɳΪ2ΣΪ2*2 > 3*1
    if(length - timesOf3 * 3 == 1)
        timesOf3 -= 1;

    int timesOf2 = (length - timesOf3 * 3) / 2;

    return (int) (pow(3, timesOf3)) * (int) (pow(2, timesOf2));
}

// ====================Դ====================
void test(const char* testName, int length, int expected)
{
    int result1 = maxProductAfterCutting_solution1(length);
    if(result1 == expected)
        std::cout << "Solution1 for " << testName << " passed." << std::endl;
    else
        std::cout << "Solution1 for " << testName << " FAILED." << std::endl;

    int result2 = maxProductAfterCutting_solution2(length);
    if(result2 == expected)
        std::cout << "Solution2 for " << testName << " passed." << std::endl;
    else
        std::cout << "Solution2 for " << testName << " FAILED." << std::endl;
}

void test1()
{
    int length = 1;
    int expected = 0;
    test("test1", length, expected);
}

void test2()
{
    int length = 2;
    int expected = 1;
    test("test2", length, expected);
}

void test3()
{
    int length = 3;
    int expected = 2;
    test("test3", length, expected);
}

void test4()
{
    int length = 4;
    int expected = 4;
    test("test4", length, expected);
}

void test5()
{
    int length = 5;
    int expected = 6;
    test("test5", length, expected);
}

void test6()
{
    int length = 6;
    int expected = 9;
    test("test6", length, expected);
}

void test7()
{
    int length = 7;
    int expected = 12;
    test("test7", length, expected);
}

void test8()
{
    int length = 8;
    int expected = 18;
    test("test8", length, expected);
}

void test9()
{
    int length = 9;
    int expected = 27;
    test("test9", length, expected);
}

void test10()
{
    int length = 10;
    int expected = 36;
    test("test10", length, expected);
}

void test11()
{
    int length = 50;
    int expected = 86093442;
    test("test11", length, expected);
}

int main(int agrc, char* argv[])
{
    test1();
    test2();
    test3();
    test4();
    test5();
    test6();
    test7();
    test8();
    test9();
    test10();
    test11();

    return 0;
}
<|endoftext|>


<commit_before>/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#include "SettingRegistry.h"

#include <sstream>
#include <iostream> // debug IO
#include <libgen.h> // dirname
#include <string>
#include <cstring> // strtok (split string using delimiters) strcpy
#include <fstream> // ifstream (to see if file exists)

#include "rapidjson/rapidjson.h"
#include "rapidjson/document.h"
#include "rapidjson/error/en.h"
#include "rapidjson/filereadstream.h"
#include "../utils/logoutput.h"

namespace cura
{

SettingRegistry SettingRegistry::instance; // define settingRegistry

std::string SettingRegistry::toString(rapidjson::Type type)
{
    switch (type)
    {
        case rapidjson::Type::kNullType: return "null";
        case rapidjson::Type::kFalseType: return "false";
        case rapidjson::Type::kTrueType: return "true";
        case rapidjson::Type::kObjectType: return "object";
        case rapidjson::Type::kArrayType: return "array";
        case rapidjson::Type::kStringType: return "string";
        case rapidjson::Type::kNumberType: return "number";
        default: return "Unknown";
    }
}

SettingConfig::SettingConfig(std::string key, std::string label)
: SettingContainer(key, label)
{
//     std::cerr << key << std::endl; // debug output to show all frontend registered settings...
}

bool SettingRegistry::settingExists(std::string key) const
{
    return setting_key_to_config.find(key) != setting_key_to_config.end();
}

SettingConfig* SettingRegistry::getSettingConfig(std::string key) const
{
    auto it = setting_key_to_config.find(key);
    if (it == setting_key_to_config.end())
        return nullptr;
    return it->second;
}

SettingRegistry::SettingRegistry()
: setting_definitions("settings", "Settings")
{
    // load search paths from environment variable CURA_ENGINE_SEARCH_PATH
    char* paths = getenv("CURA_ENGINE_SEARCH_PATH");
    if (paths)
    {
#if defined(__linux__) || (defined(__APPLE__) && defined(__MACH__))
        char delims[] = ":"; // colon
#else
        char delims[] = ";"; // semicolon
#endif
        char* path = strtok(paths, delims); // search for next path delimited by any of the characters in delims
        while (path != NULL)
        {
            search_paths.emplace(path);
            path = strtok(NULL, ";:,"); // continue searching in last call to strtok
        }
    }
}

int SettingRegistry::loadJSON(std::string filename, rapidjson::Document& json_document)
{
    FILE* f = fopen(filename.c_str(), "rb");
    if (!f)
    {
        cura::logError("Couldn't open JSON file.\n");
        return 1;
    }
    char read_buffer[4096];
    rapidjson::FileReadStream reader_stream(f, read_buffer, sizeof(read_buffer));
    json_document.ParseStream(reader_stream);
    fclose(f);
    if (json_document.HasParseError())
    {
        cura::logError("Error parsing JSON(offset %u): %s\n", (unsigned)json_document.GetErrorOffset(), GetParseError_En(json_document.GetParseError()));
        return 2;
    }

    return 0;
}

/*!
 * Check whether a file exists.
 * from https://techoverflow.net/blog/2013/01/11/cpp-check-if-file-exists/
 *
 * \param filename The path to a filename to check if it exists
 * \return Whether the file exists.
 */
bool fexists(const char *filename)
{
    std::ifstream ifile(filename);
    return (bool)ifile;
}

bool SettingRegistry::getDefinitionFile(const std::string machine_id, std::string& result)
{
    for (const std::string& search_path : search_paths)
    {
        result = search_path + std::string("/") + machine_id + std::string(".def.json");
        if (fexists(result.c_str()))
        {
            return true;
        }
    }
    return false;
}

int SettingRegistry::loadExtruderJSONsettings(unsigned int extruder_nr, SettingsBase* settings_base)
{
    if (extruder_train_ids.empty())
    {
        logError("Couldn't find any extruder trains!\n");
        return -1;
    }
    if (extruder_nr >= extruder_train_ids.size())
    {
        logWarning("Couldn't load extruder.def.json file for extruder %i. Index out of bounds.\n Loading first extruder definition instead.\n", extruder_nr);
        extruder_nr = 0;
    }
    std::string definition_file;
    bool found = getDefinitionFile(extruder_train_ids[extruder_nr], definition_file);
    if (!found)
    {
        logError("Couldn't find extruder.def.json file for extruder %i.\n", extruder_nr);
        return -1;
    }
    bool warn_base_file_duplicates = false;
    return loadJSONsettings(definition_file, settings_base, warn_base_file_duplicates);
}

int SettingRegistry::loadJSONsettings(std::string filename, SettingsBase* settings_base, bool warn_base_file_duplicates)
{
    rapidjson::Document json_document;

    log("Loading %s...\n", filename.c_str());

    int err = loadJSON(filename, json_document);
    if (err) { return err; }

    { // add parent folder to search paths
        char filename_cstr[filename.size()];
        std::strcpy(filename_cstr, filename.c_str()); // copy the string because dirname(.) changes the input string!!!
        std::string folder_name = std::string(dirname(filename_cstr));
        search_paths.emplace(folder_name);
    }

    if (json_document.HasMember("inherits") && json_document["inherits"].IsString())
    {
        std::string child_filename;
        bool found = getDefinitionFile(json_document["inherits"].GetString(), child_filename);
        if (!found)
        {
            cura::logError("Inherited JSON file \"%s\" not found\n", json_document["inherits"].GetString());
            return -1;
        }
        err = loadJSONsettings(child_filename, settings_base, warn_base_file_duplicates); // load child first
        if (err)
        {
            return err;
        }
        err = loadJSONsettingsFromDoc(json_document, settings_base, false);
    }
    else
    {
        err = loadJSONsettingsFromDoc(json_document, settings_base, warn_base_file_duplicates);
    }

    if (json_document.HasMember("metadata") && json_document["metadata"].IsObject())
    {
        const rapidjson::Value& json_metadata = json_document["metadata"];
        if (json_metadata.HasMember("machine_extruder_trains") && json_metadata["machine_extruder_trains"].IsObject())
        {
            const rapidjson::Value& json_machine_extruder_trains = json_metadata["machine_extruder_trains"];
            for (rapidjson::Value::ConstMemberIterator extr_train_iterator = json_machine_extruder_trains.MemberBegin(); extr_train_iterator != json_machine_extruder_trains.MemberEnd(); ++extr_train_iterator)
            {
                int extruder_train_nr = atoi(extr_train_iterator->name.GetString());
                if (extruder_train_nr < 0)
                {
                    continue;
                }
                const rapidjson::Value& json_id = extr_train_iterator->value;
                if (!json_id.IsString())
                {
                    continue;
                }
                const char* id = json_id.GetString();
                if (extruder_train_nr >= (int) extruder_train_ids.size())
                {
                    extruder_train_ids.resize(extruder_train_nr + 1);
                }
                extruder_train_ids[extruder_train_nr] = std::string(id);
            }
        }
    }

    return err;
}

int SettingRegistry::loadJSONsettingsFromDoc(rapidjson::Document& json_document, SettingsBase* settings_base, bool warn_duplicates)
{

    if (!json_document.IsObject())
    {
        cura::logError("JSON file is not an object.\n");
        return 3;
    }

    if (json_document.HasMember("settings"))
    {
        handleChildren(json_document["settings"], settings_base, warn_duplicates);
    }

    if (json_document.HasMember("overrides"))
    {
        const rapidjson::Value& json_object_container = json_document["overrides"];
        for (rapidjson::Value::ConstMemberIterator override_iterator = json_object_container.MemberBegin(); override_iterator != json_object_container.MemberEnd(); ++override_iterator)
        {
            std::string setting = override_iterator->name.GetString();
            SettingConfig* conf = getSettingConfig(setting);
            if (!conf) //Setting could not be found.
            {
                logWarning("Trying to override unknown setting %s.\n", setting.c_str());
                continue;
            }
            _loadSettingValues(conf, override_iterator, settings_base);
        }
    }

    return 0;
}

void SettingRegistry::handleChildren(const rapidjson::Value& settings_list, SettingsBase* settings_base, bool warn_duplicates)
{
    if (!settings_list.IsObject())
    {
        logError("json settings list is not an object!\n");
        return;
    }
    for (rapidjson::Value::ConstMemberIterator setting_iterator = settings_list.MemberBegin(); setting_iterator != settings_list.MemberEnd(); ++setting_iterator)
    {
        handleSetting(setting_iterator, settings_base, warn_duplicates);
        if (setting_iterator->value.HasMember("children"))
        {
            handleChildren(setting_iterator->value["children"], settings_base, warn_duplicates);
        }
    }
}

bool SettingRegistry::settingIsUsedByEngine(const rapidjson::Value& setting)
{
    if (setting.HasMember("children"))
    {
        return false;
    }
    else
    {
        return true;
    }
}

void SettingRegistry::handleSetting(const rapidjson::Value::ConstMemberIterator& json_setting_it, SettingsBase* settings_base, bool warn_duplicates)
{
    const rapidjson::Value& json_setting = json_setting_it->value;
    if (!json_setting.IsObject())
    {
        logError("json setting is not an object!\n");
        return;
    }
    std::string name = json_setting_it->name.GetString();
    if (json_setting.HasMember("type") && json_setting["type"].IsString() && json_setting["type"].GetString() == std::string("category"))
    { // skip category objects
        setting_key_to_config[name] = nullptr; // add the category name to the mapping, but don't instantiate a setting config for it.
        return;
    }
    if (settingIsUsedByEngine(json_setting))
    {
        if (!json_setting.HasMember("label") || !json_setting["label"].IsString())
        {
            logError("json setting \"%s\" has no label!\n", name.c_str());
            return;
        }
        std::string label = json_setting["label"].GetString();

        SettingConfig* setting = getSettingConfig(name);
        if (warn_duplicates && setting)
        {
            cura::logWarning("Duplicate definition of setting: %s a.k.a. \"%s\" was already claimed by \"%s\"\n", name.c_str(), label.c_str(), getSettingConfig(name)->getLabel().c_str());
        }
        if (!setting)
        {
            setting = &addSetting(name, label);
        }
        _loadSettingValues(setting, json_setting_it, settings_base);
    }
    else
    {
        setting_key_to_config[name] = nullptr; // add the setting name to the mapping, but don't instantiate a setting config for it.
    }
}

SettingConfig& SettingRegistry::addSetting(std::string name, std::string label)
{
    SettingConfig* config = setting_definitions.addChild(name, label);

    setting_key_to_config[name] = config;

    return *config;
}

void SettingRegistry::loadDefault(const rapidjson::GenericValue< rapidjson::UTF8< char > >::ConstMemberIterator& json_object_it, SettingConfig* config)
{
    const rapidjson::Value& setting_content = json_object_it->value;
    if (setting_content.HasMember("default_value"))
    {
        const rapidjson::Value& dflt = setting_content["default_value"];
        if (dflt.IsString())
        {
            config->setDefault(dflt.GetString());
        }
        else if (dflt.IsTrue())
        {
            config->setDefault("true");
        }
        else if (dflt.IsFalse())
        {
            config->setDefault("false");
        }
        else if (dflt.IsNumber())
        {
            std::ostringstream ss;
            ss << dflt.GetDouble();
            config->setDefault(ss.str());
        } // arrays are ignored because machine_extruder_trains needs to be handled separately
        else
        {
            logWarning("WARNING: Unrecognized data type in JSON: %s has type %s\n", json_object_it->name.GetString(), toString(dflt.GetType()).c_str());
        }
    }
}

void SettingRegistry::_loadSettingValues(SettingConfig* config, const rapidjson::GenericValue< rapidjson::UTF8< char > >::ConstMemberIterator& json_object_it, SettingsBase* settings_base)
{
    const rapidjson::Value& data = json_object_it->value;
    /// Fill the setting config object with data we have in the json file.
    if (data.HasMember("type") && data["type"].IsString())
    {
        config->setType(data["type"].GetString());
    }
    if (config->getType() == std::string("polygon") || config->getType() == std::string("polygons"))
    { // skip polygon settings : not implemented yet and not used yet (TODO)
//         logWarning("WARNING: Loading polygon setting %s not implemented...\n", json_object_it->name.GetString());
        return;
    }

    loadDefault(json_object_it, config);

    if (data.HasMember("unit") && data["unit"].IsString())
    {
        config->setUnit(data["unit"].GetString());
    }

    settings_base->_setSetting(config->getKey(), config->getDefaultValue());
}

}//namespace cura
<commit_msg>Remove the duplicate SettingConfig definition.<commit_after>/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#include "SettingRegistry.h"

#include <sstream>
#include <iostream> // debug IO
#include <libgen.h> // dirname
#include <string>
#include <cstring> // strtok (split string using delimiters) strcpy
#include <fstream> // ifstream (to see if file exists)

#include "rapidjson/rapidjson.h"
#include "rapidjson/document.h"
#include "rapidjson/error/en.h"
#include "rapidjson/filereadstream.h"
#include "../utils/logoutput.h"

namespace cura
{

SettingRegistry SettingRegistry::instance; // define settingRegistry

std::string SettingRegistry::toString(rapidjson::Type type)
{
    switch (type)
    {
        case rapidjson::Type::kNullType: return "null";
        case rapidjson::Type::kFalseType: return "false";
        case rapidjson::Type::kTrueType: return "true";
        case rapidjson::Type::kObjectType: return "object";
        case rapidjson::Type::kArrayType: return "array";
        case rapidjson::Type::kStringType: return "string";
        case rapidjson::Type::kNumberType: return "number";
        default: return "Unknown";
    }
}

bool SettingRegistry::settingExists(std::string key) const
{
    return setting_key_to_config.find(key) != setting_key_to_config.end();
}

SettingConfig* SettingRegistry::getSettingConfig(std::string key) const
{
    auto it = setting_key_to_config.find(key);
    if (it == setting_key_to_config.end())
        return nullptr;
    return it->second;
}

SettingRegistry::SettingRegistry()
: setting_definitions("settings", "Settings")
{
    // load search paths from environment variable CURA_ENGINE_SEARCH_PATH
    char* paths = getenv("CURA_ENGINE_SEARCH_PATH");
    if (paths)
    {
#if defined(__linux__) || (defined(__APPLE__) && defined(__MACH__))
        char delims[] = ":"; // colon
#else
        char delims[] = ";"; // semicolon
#endif
        char* path = strtok(paths, delims); // search for next path delimited by any of the characters in delims
        while (path != NULL)
        {
            search_paths.emplace(path);
            path = strtok(NULL, ";:,"); // continue searching in last call to strtok
        }
    }
}

int SettingRegistry::loadJSON(std::string filename, rapidjson::Document& json_document)
{
    FILE* f = fopen(filename.c_str(), "rb");
    if (!f)
    {
        cura::logError("Couldn't open JSON file.\n");
        return 1;
    }
    char read_buffer[4096];
    rapidjson::FileReadStream reader_stream(f, read_buffer, sizeof(read_buffer));
    json_document.ParseStream(reader_stream);
    fclose(f);
    if (json_document.HasParseError())
    {
        cura::logError("Error parsing JSON(offset %u): %s\n", (unsigned)json_document.GetErrorOffset(), GetParseError_En(json_document.GetParseError()));
        return 2;
    }

    return 0;
}

/*!
 * Check whether a file exists.
 * from https://techoverflow.net/blog/2013/01/11/cpp-check-if-file-exists/
 *
 * \param filename The path to a filename to check if it exists
 * \return Whether the file exists.
 */
bool fexists(const char *filename)
{
    std::ifstream ifile(filename);
    return (bool)ifile;
}

bool SettingRegistry::getDefinitionFile(const std::string machine_id, std::string& result)
{
    for (const std::string& search_path : search_paths)
    {
        result = search_path + std::string("/") + machine_id + std::string(".def.json");
        if (fexists(result.c_str()))
        {
            return true;
        }
    }
    return false;
}

int SettingRegistry::loadExtruderJSONsettings(unsigned int extruder_nr, SettingsBase* settings_base)
{
    if (extruder_train_ids.empty())
    {
        logError("Couldn't find any extruder trains!\n");
        return -1;
    }
    if (extruder_nr >= extruder_train_ids.size())
    {
        logWarning("Couldn't load extruder.def.json file for extruder %i. Index out of bounds.\n Loading first extruder definition instead.\n", extruder_nr);
        extruder_nr = 0;
    }
    std::string definition_file;
    bool found = getDefinitionFile(extruder_train_ids[extruder_nr], definition_file);
    if (!found)
    {
        logError("Couldn't find extruder.def.json file for extruder %i.\n", extruder_nr);
        return -1;
    }
    bool warn_base_file_duplicates = false;
    return loadJSONsettings(definition_file, settings_base, warn_base_file_duplicates);
}

int SettingRegistry::loadJSONsettings(std::string filename, SettingsBase* settings_base, bool warn_base_file_duplicates)
{
    rapidjson::Document json_document;

    log("Loading %s...\n", filename.c_str());

    int err = loadJSON(filename, json_document);
    if (err) { return err; }

    { // add parent folder to search paths
        char filename_cstr[filename.size()];
        std::strcpy(filename_cstr, filename.c_str()); // copy the string because dirname(.) changes the input string!!!
        std::string folder_name = std::string(dirname(filename_cstr));
        search_paths.emplace(folder_name);
    }

    if (json_document.HasMember("inherits") && json_document["inherits"].IsString())
    {
        std::string child_filename;
        bool found = getDefinitionFile(json_document["inherits"].GetString(), child_filename);
        if (!found)
        {
            cura::logError("Inherited JSON file \"%s\" not found\n", json_document["inherits"].GetString());
            return -1;
        }
        err = loadJSONsettings(child_filename, settings_base, warn_base_file_duplicates); // load child first
        if (err)
        {
            return err;
        }
        err = loadJSONsettingsFromDoc(json_document, settings_base, false);
    }
    else
    {
        err = loadJSONsettingsFromDoc(json_document, settings_base, warn_base_file_duplicates);
    }

    if (json_document.HasMember("metadata") && json_document["metadata"].IsObject())
    {
        const rapidjson::Value& json_metadata = json_document["metadata"];
        if (json_metadata.HasMember("machine_extruder_trains") && json_metadata["machine_extruder_trains"].IsObject())
        {
            const rapidjson::Value& json_machine_extruder_trains = json_metadata["machine_extruder_trains"];
            for (rapidjson::Value::ConstMemberIterator extr_train_iterator = json_machine_extruder_trains.MemberBegin(); extr_train_iterator != json_machine_extruder_trains.MemberEnd(); ++extr_train_iterator)
            {
                int extruder_train_nr = atoi(extr_train_iterator->name.GetString());
                if (extruder_train_nr < 0)
                {
                    continue;
                }
                const rapidjson::Value& json_id = extr_train_iterator->value;
                if (!json_id.IsString())
                {
                    continue;
                }
                const char* id = json_id.GetString();
                if (extruder_train_nr >= (int) extruder_train_ids.size())
                {
                    extruder_train_ids.resize(extruder_train_nr + 1);
                }
                extruder_train_ids[extruder_train_nr] = std::string(id);
            }
        }
    }

    return err;
}

int SettingRegistry::loadJSONsettingsFromDoc(rapidjson::Document& json_document, SettingsBase* settings_base, bool warn_duplicates)
{

    if (!json_document.IsObject())
    {
        cura::logError("JSON file is not an object.\n");
        return 3;
    }

    if (json_document.HasMember("settings"))
    {
        handleChildren(json_document["settings"], settings_base, warn_duplicates);
    }

    if (json_document.HasMember("overrides"))
    {
        const rapidjson::Value& json_object_container = json_document["overrides"];
        for (rapidjson::Value::ConstMemberIterator override_iterator = json_object_container.MemberBegin(); override_iterator != json_object_container.MemberEnd(); ++override_iterator)
        {
            std::string setting = override_iterator->name.GetString();
            SettingConfig* conf = getSettingConfig(setting);
            if (!conf) //Setting could not be found.
            {
                logWarning("Trying to override unknown setting %s.\n", setting.c_str());
                continue;
            }
            _loadSettingValues(conf, override_iterator, settings_base);
        }
    }

    return 0;
}

void SettingRegistry::handleChildren(const rapidjson::Value& settings_list, SettingsBase* settings_base, bool warn_duplicates)
{
    if (!settings_list.IsObject())
    {
        logError("json settings list is not an object!\n");
        return;
    }
    for (rapidjson::Value::ConstMemberIterator setting_iterator = settings_list.MemberBegin(); setting_iterator != settings_list.MemberEnd(); ++setting_iterator)
    {
        handleSetting(setting_iterator, settings_base, warn_duplicates);
        if (setting_iterator->value.HasMember("children"))
        {
            handleChildren(setting_iterator->value["children"], settings_base, warn_duplicates);
        }
    }
}

bool SettingRegistry::settingIsUsedByEngine(const rapidjson::Value& setting)
{
    if (setting.HasMember("children"))
    {
        return false;
    }
    else
    {
        return true;
    }
}

void SettingRegistry::handleSetting(const rapidjson::Value::ConstMemberIterator& json_setting_it, SettingsBase* settings_base, bool warn_duplicates)
{
    const rapidjson::Value& json_setting = json_setting_it->value;
    if (!json_setting.IsObject())
    {
        logError("json setting is not an object!\n");
        return;
    }
    std::string name = json_setting_it->name.GetString();
    if (json_setting.HasMember("type") && json_setting["type"].IsString() && json_setting["type"].GetString() == std::string("category"))
    { // skip category objects
        setting_key_to_config[name] = nullptr; // add the category name to the mapping, but don't instantiate a setting config for it.
        return;
    }
    if (settingIsUsedByEngine(json_setting))
    {
        if (!json_setting.HasMember("label") || !json_setting["label"].IsString())
        {
            logError("json setting \"%s\" has no label!\n", name.c_str());
            return;
        }
        std::string label = json_setting["label"].GetString();

        SettingConfig* setting = getSettingConfig(name);
        if (warn_duplicates && setting)
        {
            cura::logWarning("Duplicate definition of setting: %s a.k.a. \"%s\" was already claimed by \"%s\"\n", name.c_str(), label.c_str(), getSettingConfig(name)->getLabel().c_str());
        }
        if (!setting)
        {
            setting = &addSetting(name, label);
        }
        _loadSettingValues(setting, json_setting_it, settings_base);
    }
    else
    {
        setting_key_to_config[name] = nullptr; // add the setting name to the mapping, but don't instantiate a setting config for it.
    }
}

SettingConfig& SettingRegistry::addSetting(std::string name, std::string label)
{
    SettingConfig* config = setting_definitions.addChild(name, label);

    setting_key_to_config[name] = config;

    return *config;
}

void SettingRegistry::loadDefault(const rapidjson::GenericValue< rapidjson::UTF8< char > >::ConstMemberIterator& json_object_it, SettingConfig* config)
{
    const rapidjson::Value& setting_content = json_object_it->value;
    if (setting_content.HasMember("default_value"))
    {
        const rapidjson::Value& dflt = setting_content["default_value"];
        if (dflt.IsString())
        {
            config->setDefault(dflt.GetString());
        }
        else if (dflt.IsTrue())
        {
            config->setDefault("true");
        }
        else if (dflt.IsFalse())
        {
            config->setDefault("false");
        }
        else if (dflt.IsNumber())
        {
            std::ostringstream ss;
            ss << dflt.GetDouble();
            config->setDefault(ss.str());
        } // arrays are ignored because machine_extruder_trains needs to be handled separately
        else
        {
            logWarning("WARNING: Unrecognized data type in JSON: %s has type %s\n", json_object_it->name.GetString(), toString(dflt.GetType()).c_str());
        }
    }
}

void SettingRegistry::_loadSettingValues(SettingConfig* config, const rapidjson::GenericValue< rapidjson::UTF8< char > >::ConstMemberIterator& json_object_it, SettingsBase* settings_base)
{
    const rapidjson::Value& data = json_object_it->value;
    /// Fill the setting config object with data we have in the json file.
    if (data.HasMember("type") && data["type"].IsString())
    {
        config->setType(data["type"].GetString());
    }
    if (config->getType() == std::string("polygon") || config->getType() == std::string("polygons"))
    { // skip polygon settings : not implemented yet and not used yet (TODO)
//         logWarning("WARNING: Loading polygon setting %s not implemented...\n", json_object_it->name.GetString());
        return;
    }

    loadDefault(json_object_it, config);

    if (data.HasMember("unit") && data["unit"].IsString())
    {
        config->setUnit(data["unit"].GetString());
    }

    settings_base->_setSetting(config->getKey(), config->getDefaultValue());
}

}//namespace cura
<|endoftext|>


<commit_before>#include "mitkPointSetWriter.h"
#include <iostream>
#include <fstream>

//
// Initialization of the xml tags.
//

const char* mitk::PointSetWriter::XML_POINT_SET_FILE = "point_set_file" ;

const char* mitk::PointSetWriter::XML_FILE_VERSION = "file_version" ;

const char* mitk::PointSetWriter::XML_POINT_SET = "point_set" ;

const char* mitk::PointSetWriter::XML_POINT = "point" ;

const char* mitk::PointSetWriter::XML_ID = "id" ;

const char* mitk::PointSetWriter::XML_X = "x" ;

const char* mitk::PointSetWriter::XML_Y = "y" ;

const char* mitk::PointSetWriter::XML_Z = "z" ;

const char* mitk::PointSetWriter::VERSION_STRING = "0.1" ;

mitk::PointSetWriter::PointSetWriter()
{
    this->SetNumberOfRequiredInputs( 1 );
    this->SetNumberOfOutputs( 1 );
    this->SetNthOutput( 0, mitk::PointSet::New().GetPointer() );
    m_Indent = 2;
    m_IndentDepth = 0;
}

mitk::PointSetWriter::~PointSetWriter()
{}

void mitk::PointSetWriter::GenerateData()
{
    m_IndentDepth = 0;

    //
    // Opening the file to write to
    //
    if ( m_FileName == "" )
    {
        itkWarningMacro( << "Sorry, filename has not been set!" );
        return ;
    }
    std::ofstream out( m_FileName.c_str() );
    if ( !out.good() )
    {
        itkWarningMacro( << "Sorry, file " << m_FileName << " could not be opened!" );
        out.close();
        return ;
    }

    //
    // Here the actual xml writing begins
    //
    WriteXMLHeader( out );
    WriteStartElement( XML_POINT_SET_FILE, out );
    WriteStartElement( XML_FILE_VERSION, out );
    WriteCharacterData( VERSION_STRING, out );
    WriteEndElement( XML_FILE_VERSION, out, false );

    //
    // for each input object write its xml representation to
    // the stream
    //
    for ( unsigned int i = 0 ; i < this->GetNumberOfInputs(); ++i )
    {
        InputType::Pointer pointSet = this->GetInput( i );
        assert( pointSet.IsNotNull() );
        WriteXML( pointSet.GetPointer(), out );
    }

    WriteEndElement( XML_POINT_SET_FILE, out );

    out.close();
}

void mitk::PointSetWriter::WriteXML( mitk::PointSet* pointSet, std::ofstream& out )
{
    WriteStartElement( XML_POINT_SET, out );
    mitk::PointSet::PointsContainer* pointsContainer = pointSet->GetPointSet()->GetPoints();
    mitk::PointSet::PointsContainer::Iterator it, end;
    for ( it = pointsContainer->Begin(); it != pointsContainer->End(); ++it )
    {
        WriteStartElement( XML_POINT, out );

        WriteStartElement( XML_ID, out );
        WriteCharacterData( ConvertToString<int>( it->Index() ).c_str() , out );
        WriteEndElement( XML_ID, out, false );

        mitk::PointSet::PointType point = it->Value();

        WriteStartElement( XML_X, out );
        WriteCharacterData( ConvertToString<mitk::ScalarType>( point[ 0 ] ).c_str(), out );
        WriteEndElement( XML_X, out, false );

        WriteStartElement( XML_Y, out );
        WriteCharacterData( ConvertToString<mitk::ScalarType>( point[ 1 ] ).c_str(), out );
        WriteEndElement( XML_Y, out, false );

        WriteStartElement( XML_Z, out );
        WriteCharacterData( ConvertToString<mitk::ScalarType>( point[ 2 ] ).c_str(), out );
        WriteEndElement( XML_Z, out, false );

        WriteEndElement( XML_POINT, out );
    }
    WriteEndElement( XML_POINT_SET, out );
}

void mitk::PointSetWriter::ResizeInputs( const unsigned int& num )
{
    unsigned int prevNum = this->GetNumberOfInputs();
    this->SetNumberOfInputs( num );
    for ( unsigned int i = prevNum; i < num; ++i )
    {
        this->SetNthInput( i, mitk::PointSet::New().GetPointer() );
    }
}

void mitk::PointSetWriter::SetInput( InputType* pointSet )
{
    this->ProcessObject::SetNthInput( 0, pointSet );
}

void mitk::PointSetWriter::SetInput( const unsigned int& id, InputType* pointSet )
{
    if ( id >= this->GetNumberOfInputs() )
        this->ResizeInputs( id + 1 );
    this->ProcessObject::SetNthInput( id, pointSet );
}

mitk::PointSet* mitk::PointSetWriter::GetInput()
{
    if ( this->GetNumberOfInputs() < 1 )
    {
        return 0;
    }
    else
    {
        return dynamic_cast<InputType*> ( this->GetInput( 0 ) );
    }
}

mitk::PointSet* mitk::PointSetWriter::GetInput( const unsigned int& num )
{
    return dynamic_cast<InputType*> ( this->ProcessObject::GetInput( num ) );
}

template < typename T>
std::string mitk::PointSetWriter::ConvertToString( T value )
{
    std::ostringstream o;
    if ( o << value )
        return o.str();
    else
        return "conversion error";
}

void mitk::PointSetWriter::WriteXMLHeader( std::ofstream &file )
{
    file << "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>";
}

void mitk::PointSetWriter::WriteStartElement( const char *const tag, std::ofstream &file )
{
    file << std::endl;
    WriteIndent( file );
    file << '<' << tag << '>';
    m_IndentDepth++;
}

void mitk::PointSetWriter::WriteEndElement( const char *const tag, std::ofstream &file, const bool& indent )
{
    m_IndentDepth--;
    if ( indent )
    {
        file << std::endl;
        WriteIndent( file );
    }
    file << '<' << '/' << tag << '>';
}

void mitk::PointSetWriter::WriteCharacterData( const char *const data, std::ofstream &file )
{
    file << data;
}

void mitk::PointSetWriter::WriteStartElement( std::string &tag, std::ofstream &file )
{
    WriteStartElement( tag.c_str(), file );
}

void mitk::PointSetWriter::WriteEndElement( std::string &tag, std::ofstream &file, const bool& indent )
{
    WriteEndElement( tag.c_str(), file, indent );
}

void mitk::PointSetWriter::WriteCharacterData( std::string &data, std::ofstream &file )
{
    WriteCharacterData( data.c_str(), file );
}

void mitk::PointSetWriter::WriteIndent( std::ofstream& file )
{
    std::string spaces( m_IndentDepth * m_Indent, ' ' );
    file << spaces.c_str();
}
<commit_msg>Added missing newline at end of file.<commit_after>#include "mitkPointSetWriter.h"
#include <iostream>
#include <fstream>

//
// Initialization of the xml tags.
//

const char* mitk::PointSetWriter::XML_POINT_SET_FILE = "point_set_file" ;

const char* mitk::PointSetWriter::XML_FILE_VERSION = "file_version" ;

const char* mitk::PointSetWriter::XML_POINT_SET = "point_set" ;

const char* mitk::PointSetWriter::XML_POINT = "point" ;

const char* mitk::PointSetWriter::XML_ID = "id" ;

const char* mitk::PointSetWriter::XML_X = "x" ;

const char* mitk::PointSetWriter::XML_Y = "y" ;

const char* mitk::PointSetWriter::XML_Z = "z" ;

const char* mitk::PointSetWriter::VERSION_STRING = "0.1" ;

mitk::PointSetWriter::PointSetWriter()
{
    this->SetNumberOfRequiredInputs( 1 );
    this->SetNumberOfOutputs( 1 );
    this->SetNthOutput( 0, mitk::PointSet::New().GetPointer() );
    m_Indent = 2;
    m_IndentDepth = 0;
}

mitk::PointSetWriter::~PointSetWriter()
{}

void mitk::PointSetWriter::GenerateData()
{
    m_IndentDepth = 0;

    //
    // Opening the file to write to
    //
    if ( m_FileName == "" )
    {
        itkWarningMacro( << "Sorry, filename has not been set!" );
        return ;
    }
    std::ofstream out( m_FileName.c_str() );
    if ( !out.good() )
    {
        itkWarningMacro( << "Sorry, file " << m_FileName << " could not be opened!" );
        out.close();
        return ;
    }

    //
    // Here the actual xml writing begins
    //
    WriteXMLHeader( out );
    WriteStartElement( XML_POINT_SET_FILE, out );
    WriteStartElement( XML_FILE_VERSION, out );
    WriteCharacterData( VERSION_STRING, out );
    WriteEndElement( XML_FILE_VERSION, out, false );

    //
    // for each input object write its xml representation to
    // the stream
    //
    for ( unsigned int i = 0 ; i < this->GetNumberOfInputs(); ++i )
    {
        InputType::Pointer pointSet = this->GetInput( i );
        assert( pointSet.IsNotNull() );
        WriteXML( pointSet.GetPointer(), out );
    }

    WriteEndElement( XML_POINT_SET_FILE, out );

    out.close();
}

void mitk::PointSetWriter::WriteXML( mitk::PointSet* pointSet, std::ofstream& out )
{
    WriteStartElement( XML_POINT_SET, out );
    mitk::PointSet::PointsContainer* pointsContainer = pointSet->GetPointSet()->GetPoints();
    mitk::PointSet::PointsContainer::Iterator it, end;
    for ( it = pointsContainer->Begin(); it != pointsContainer->End(); ++it )
    {
        WriteStartElement( XML_POINT, out );

        WriteStartElement( XML_ID, out );
        WriteCharacterData( ConvertToString<int>( it->Index() ).c_str() , out );
        WriteEndElement( XML_ID, out, false );

        mitk::PointSet::PointType point = it->Value();

        WriteStartElement( XML_X, out );
        WriteCharacterData( ConvertToString<mitk::ScalarType>( point[ 0 ] ).c_str(), out );
        WriteEndElement( XML_X, out, false );

        WriteStartElement( XML_Y, out );
        WriteCharacterData( ConvertToString<mitk::ScalarType>( point[ 1 ] ).c_str(), out );
        WriteEndElement( XML_Y, out, false );

        WriteStartElement( XML_Z, out );
        WriteCharacterData( ConvertToString<mitk::ScalarType>( point[ 2 ] ).c_str(), out );
        WriteEndElement( XML_Z, out, false );

        WriteEndElement( XML_POINT, out );
    }
    WriteEndElement( XML_POINT_SET, out );
}

void mitk::PointSetWriter::ResizeInputs( const unsigned int& num )
{
    unsigned int prevNum = this->GetNumberOfInputs();
    this->SetNumberOfInputs( num );
    for ( unsigned int i = prevNum; i < num; ++i )
    {
        this->SetNthInput( i, mitk::PointSet::New().GetPointer() );
    }
}

void mitk::PointSetWriter::SetInput( InputType* pointSet )
{
    this->ProcessObject::SetNthInput( 0, pointSet );
}

void mitk::PointSetWriter::SetInput( const unsigned int& id, InputType* pointSet )
{
    if ( id >= this->GetNumberOfInputs() )
        this->ResizeInputs( id + 1 );
    this->ProcessObject::SetNthInput( id, pointSet );
}

mitk::PointSet* mitk::PointSetWriter::GetInput()
{
    if ( this->GetNumberOfInputs() < 1 )
    {
        return 0;
    }
    else
    {
        return dynamic_cast<InputType*> ( this->GetInput( 0 ) );
    }
}

mitk::PointSet* mitk::PointSetWriter::GetInput( const unsigned int& num )
{
    return dynamic_cast<InputType*> ( this->ProcessObject::GetInput( num ) );
}

template < typename T>
std::string mitk::PointSetWriter::ConvertToString( T value )
{
    std::ostringstream o;
    if ( o << value )
        return o.str();
    else
        return "conversion error";
}

void mitk::PointSetWriter::WriteXMLHeader( std::ofstream &file )
{
    file << "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>";
}

void mitk::PointSetWriter::WriteStartElement( const char *const tag, std::ofstream &file )
{
    file << std::endl;
    WriteIndent( file );
    file << '<' << tag << '>';
    m_IndentDepth++;
}

void mitk::PointSetWriter::WriteEndElement( const char *const tag, std::ofstream &file, const bool& indent )
{
    m_IndentDepth--;
    if ( indent )
    {
        file << std::endl;
        WriteIndent( file );
    }
    file << '<' << '/' << tag << '>';
}

void mitk::PointSetWriter::WriteCharacterData( const char *const data, std::ofstream &file )
{
    file << data;
}

void mitk::PointSetWriter::WriteStartElement( std::string &tag, std::ofstream &file )
{
    WriteStartElement( tag.c_str(), file );
}

void mitk::PointSetWriter::WriteEndElement( std::string &tag, std::ofstream &file, const bool& indent )
{
    WriteEndElement( tag.c_str(), file, indent );
}

void mitk::PointSetWriter::WriteCharacterData( std::string &data, std::ofstream &file )
{
    WriteCharacterData( data.c_str(), file );
}

void mitk::PointSetWriter::WriteIndent( std::ofstream& file )
{
    std::string spaces( m_IndentDepth * m_Indent, ' ' );
    file << spaces.c_str();
}

<|endoftext|>


<commit_before>/***********************************************************************
 options.cpp - Implements the Option class hierarchy.

 Copyright (c) 2007 by Educational Technology Resources, Inc.  Others
 may also hold copyrights on code in this file.  See the CREDITS
 file in the top directory of the distribution for details.

 This file is part of MySQL++.

 MySQL++ is free software; you can redistribute it and/or modify it
 under the terms of the GNU Lesser General Public License as published
 by the Free Software Foundation; either version 2.1 of the License, or
 (at your option) any later version.

 MySQL++ is distributed in the hope that it will be useful, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
 License for more details.

 You should have received a copy of the GNU Lesser General Public
 License along with MySQL++; if not, write to the Free Software
 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
 USA
***********************************************************************/

#define MYSQLPP_NOT_HEADER
#include "options.h"

#include "dbdriver.h"

namespace mysqlpp {

#if !defined(DOXYGEN_IGNORE)
// We're hiding all the Option subclass internals from Doxygen.  All the
// upper-level classes are documented fully, and each leaf class itself
// is documented.  It's just the ctors and set() methods we're refusing
// to document over and over again.

Option::Error
CompressOption::set(DBDriver* dbd)
{
	return dbd->connected() ? Option::err_connected :
			dbd->set_option(MYSQL_OPT_COMPRESS) ?
				Option::err_NONE : Option::err_api_reject;
}

Option::Error
ConnectTimeoutOption::set(DBDriver* dbd)
{
	return dbd->connected() ? Option::err_connected :
			dbd->set_option(MYSQL_OPT_CONNECT_TIMEOUT, &arg_) ?
				Option::err_NONE : Option::err_api_reject;
}

Option::Error
FoundRowsOption::set(DBDriver* dbd)
{
	return dbd->connected() ? Option::err_connected :
			dbd->set_option(CLIENT_FOUND_ROWS, arg_) ?
				Option::err_NONE : Option::err_api_reject;
}

Option::Error
GuessConnectionOption::set(DBDriver* dbd)
{
#if MYSQL_VERSION_ID >= 40101
	return dbd->connected() ? Option::err_connected :
			dbd->set_option(MYSQL_OPT_GUESS_CONNECTION) ?
				Option::err_NONE : Option::err_api_reject;
#else
	return Option::err_api_limit;
#endif
}

Option::Error
IgnoreSpaceOption::set(DBDriver* dbd)
{
	return dbd->connected() ? Option::err_connected :
			dbd->set_option(CLIENT_IGNORE_SPACE, arg_) ?
				Option::err_NONE : Option::err_api_reject;
}

Option::Error
InitCommandOption::set(DBDriver* dbd)
{
	return dbd->connected() ? Option::err_connected :
			dbd->set_option(MYSQL_INIT_COMMAND, arg_.c_str()) ?
				Option::err_NONE : Option::err_api_reject;
}

Option::Error
InteractiveOption::set(DBDriver* dbd)
{
	return dbd->connected() ? Option::err_connected :
			dbd->set_option(CLIENT_INTERACTIVE, arg_) ?
				Option::err_NONE : Option::err_api_reject;
}

Option::Error
LocalFilesOption::set(DBDriver* dbd)
{
	return dbd->connected() ? Option::err_connected :
			dbd->set_option(CLIENT_LOCAL_FILES, arg_) ?
				Option::err_NONE : Option::err_api_reject;
}

Option::Error
LocalInfileOption::set(DBDriver* dbd)
{
	return dbd->connected() ? Option::err_connected :
			dbd->set_option(MYSQL_OPT_LOCAL_INFILE, &arg_) ?
				Option::err_NONE : Option::err_api_reject;
}

Option::Error
MultiResultsOption::set(DBDriver* dbd)
{
#if MYSQL_VERSION_ID >= 40101
	if (dbd->connected()) {
		return dbd->set_option(arg_ ? MYSQL_OPTION_MULTI_STATEMENTS_ON :
				MYSQL_OPTION_MULTI_STATEMENTS_OFF) ?
				Option::err_NONE : Option::err_api_reject;
	}
	else {
		return dbd->set_option(CLIENT_MULTI_RESULTS, arg_) ?
				Option::err_NONE : Option::err_api_reject;
	}
#else
	return Option::err_api_limit;
#endif
}

Option::Error
MultiStatementsOption::set(DBDriver* dbd)
{
#if MYSQL_VERSION_ID >= 40101
	if (dbd->connected()) {
		return dbd->set_option(arg_ ? MYSQL_OPTION_MULTI_STATEMENTS_ON :
				MYSQL_OPTION_MULTI_STATEMENTS_OFF) ?
				Option::err_NONE : Option::err_api_reject;
	}
	else {
		return dbd->set_option(CLIENT_MULTI_STATEMENTS, arg_) ?
				Option::err_NONE : Option::err_api_reject;
	}
#else
	return Option::err_api_limit;
#endif
}

Option::Error
NamedPipeOption::set(DBDriver* dbd)
{
	return dbd->connected() ? Option::err_connected :
			dbd->set_option(MYSQL_OPT_NAMED_PIPE) ?
				Option::err_NONE : Option::err_api_reject;
}

Option::Error
NoSchemaOption::set(DBDriver* dbd)
{
	return dbd->connected() ? Option::err_connected :
			dbd->set_option(CLIENT_NO_SCHEMA, arg_) ?
				Option::err_NONE : Option::err_api_reject;
}

#if MYSQL_VERSION_ID > 40000		// only in 4.0 +
Option::Error
ProtocolOption::set(DBDriver* dbd)
{
	return dbd->connected() ? Option::err_connected :
			dbd->set_option(MYSQL_OPT_PROTOCOL, &arg_) ?
				Option::err_NONE : Option::err_api_reject;
}
#endif

Option::Error
ReadDefaultFileOption::set(DBDriver* dbd)
{
	return dbd->connected() ? Option::err_connected :
			dbd->set_option(MYSQL_READ_DEFAULT_FILE, arg_.c_str()) ?
				Option::err_NONE : Option::err_api_reject;
}

Option::Error
ReadDefaultGroupOption::set(DBDriver* dbd)
{
	return dbd->connected() ? Option::err_connected :
			dbd->set_option(MYSQL_READ_DEFAULT_GROUP, arg_.c_str()) ?
				Option::err_NONE : Option::err_api_reject;
}

Option::Error
ReadTimeoutOption::set(DBDriver* dbd)
{
#if MYSQL_VERSION_ID >= 40101
	return dbd->connected() ? Option::err_connected :
			dbd->set_option(MYSQL_OPT_READ_TIMEOUT, &arg_) ?
				Option::err_NONE : Option::err_api_reject;
#else
	return Option::err_api_limit;
#endif
}

Option::Error
ReconnectOption::set(DBDriver* dbd)
{
#if MYSQL_VERSION_ID >= 50013
	return dbd->connected() ? Option::err_connected :
			dbd->set_option(MYSQL_OPT_RECONNECT, &arg_) ?
				Option::err_NONE : Option::err_api_reject;
#else
	return Option::err_api_limit;
#endif
}

Option::Error
ReportDataTruncationOption::set(DBDriver* dbd)
{
#if MYSQL_VERSION_ID >= 50003
	return dbd->connected() ? Option::err_connected :
			dbd->set_option(MYSQL_REPORT_DATA_TRUNCATION, &arg_) ?
				Option::err_NONE : Option::err_api_reject;
#else
	return Option::err_api_limit;
#endif
}

Option::Error
SecureAuthOption::set(DBDriver* dbd)
{
#if MYSQL_VERSION_ID >= 40101
	return dbd->connected() ? Option::err_connected :
			dbd->set_option(MYSQL_SECURE_AUTH, &arg_) ?
				Option::err_NONE : Option::err_api_reject;
#else
	return Option::err_api_limit;
#endif
}

Option::Error
SetCharsetDirOption::set(DBDriver* dbd)
{
	return dbd->connected() ? Option::err_connected :
			dbd->set_option(MYSQL_SET_CHARSET_DIR, arg_.c_str()) ?
				Option::err_NONE : Option::err_api_reject;
}

Option::Error
SetCharsetNameOption::set(DBDriver* dbd)
{
	return dbd->connected() ? Option::err_connected :
			dbd->set_option(MYSQL_SET_CHARSET_NAME, arg_.c_str()) ?
				Option::err_NONE : Option::err_api_reject;
}

Option::Error
SetClientIpOption::set(DBDriver* dbd)
{
#if MYSQL_VERSION_ID >= 40101
	return dbd->connected() ? Option::err_connected :
			dbd->set_option(MYSQL_SET_CLIENT_IP, arg_.c_str()) ?
				Option::err_NONE : Option::err_api_reject;
#else
	return Option::err_api_limit;
#endif
}

Option::Error
SharedMemoryBaseNameOption::set(DBDriver* dbd)
{
#if MYSQL_VERSION_ID >= 40100
	return dbd->connected() ? Option::err_connected :
			dbd->set_option(MYSQL_SHARED_MEMORY_BASE_NAME, arg_.c_str()) ?
				Option::err_NONE : Option::err_api_reject;
#else
	return Option::err_api_limit;
#endif
}

Option::Error
SslOption::set(DBDriver* dbd)
{
#if defined(HAVE_MYSQL_SSL_SET)
	return dbd->connected() ? Option::err_connected :
			dbd->enable_ssl(key_.c_str(), cert_.c_str(), ca_.c_str(),
				capath_.c_str(), cipher_.c_str()) ?
				Option::err_NONE : Option::err_api_reject;
#else
	return Option::err_api_limit;
#endif
}

Option::Error
UseEmbeddedConnectionOption::set(DBDriver* dbd)
{
#if MYSQL_VERSION_ID >= 40101
	return dbd->connected() ? Option::err_connected :
			dbd->set_option(MYSQL_OPT_USE_EMBEDDED_CONNECTION) ?
				Option::err_NONE : Option::err_api_reject;
#else
	return Option::err_api_limit;
#endif
}

Option::Error
UseRemoteConnectionOption::set(DBDriver* dbd)
{
#if MYSQL_VERSION_ID >= 40101
	return dbd->connected() ? Option::err_connected :
			dbd->set_option(MYSQL_OPT_USE_REMOTE_CONNECTION) ?
				Option::err_NONE : Option::err_api_reject;
#else
	return Option::err_api_limit;
#endif
}

Option::Error
WriteTimeoutOption::set(DBDriver* dbd)
{
#if MYSQL_VERSION_ID >= 40101
	return dbd->connected() ? Option::err_connected :
			dbd->set_option(MYSQL_OPT_WRITE_TIMEOUT, &arg_) ?
				Option::err_NONE : Option::err_api_reject;
#else
	return Option::err_api_limit;
#endif
}

#endif // !defined(DOXYGEN_IGNORE)

} // end namespace mysqlpp
<commit_msg>Passing 0 to mysql_ssl_set() for SSL parameters not given when populating SslOption object instead of an empty string.  Fixes bug #11796.  Fix by Petteri Kiiskinen <iridian@cs.hut.fi><commit_after>/***********************************************************************
 options.cpp - Implements the Option class hierarchy.

 Copyright (c) 2007 by Educational Technology Resources, Inc.  Others
 may also hold copyrights on code in this file.  See the CREDITS
 file in the top directory of the distribution for details.

 This file is part of MySQL++.

 MySQL++ is free software; you can redistribute it and/or modify it
 under the terms of the GNU Lesser General Public License as published
 by the Free Software Foundation; either version 2.1 of the License, or
 (at your option) any later version.

 MySQL++ is distributed in the hope that it will be useful, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
 License for more details.

 You should have received a copy of the GNU Lesser General Public
 License along with MySQL++; if not, write to the Free Software
 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
 USA
***********************************************************************/

#define MYSQLPP_NOT_HEADER
#include "options.h"

#include "dbdriver.h"

namespace mysqlpp {

#if !defined(DOXYGEN_IGNORE)
// We're hiding all the Option subclass internals from Doxygen.  All the
// upper-level classes are documented fully, and each leaf class itself
// is documented.  It's just the ctors and set() methods we're refusing
// to document over and over again.

Option::Error
CompressOption::set(DBDriver* dbd)
{
	return dbd->connected() ? Option::err_connected :
			dbd->set_option(MYSQL_OPT_COMPRESS) ?
				Option::err_NONE : Option::err_api_reject;
}

Option::Error
ConnectTimeoutOption::set(DBDriver* dbd)
{
	return dbd->connected() ? Option::err_connected :
			dbd->set_option(MYSQL_OPT_CONNECT_TIMEOUT, &arg_) ?
				Option::err_NONE : Option::err_api_reject;
}

Option::Error
FoundRowsOption::set(DBDriver* dbd)
{
	return dbd->connected() ? Option::err_connected :
			dbd->set_option(CLIENT_FOUND_ROWS, arg_) ?
				Option::err_NONE : Option::err_api_reject;
}

Option::Error
GuessConnectionOption::set(DBDriver* dbd)
{
#if MYSQL_VERSION_ID >= 40101
	return dbd->connected() ? Option::err_connected :
			dbd->set_option(MYSQL_OPT_GUESS_CONNECTION) ?
				Option::err_NONE : Option::err_api_reject;
#else
	return Option::err_api_limit;
#endif
}

Option::Error
IgnoreSpaceOption::set(DBDriver* dbd)
{
	return dbd->connected() ? Option::err_connected :
			dbd->set_option(CLIENT_IGNORE_SPACE, arg_) ?
				Option::err_NONE : Option::err_api_reject;
}

Option::Error
InitCommandOption::set(DBDriver* dbd)
{
	return dbd->connected() ? Option::err_connected :
			dbd->set_option(MYSQL_INIT_COMMAND, arg_.c_str()) ?
				Option::err_NONE : Option::err_api_reject;
}

Option::Error
InteractiveOption::set(DBDriver* dbd)
{
	return dbd->connected() ? Option::err_connected :
			dbd->set_option(CLIENT_INTERACTIVE, arg_) ?
				Option::err_NONE : Option::err_api_reject;
}

Option::Error
LocalFilesOption::set(DBDriver* dbd)
{
	return dbd->connected() ? Option::err_connected :
			dbd->set_option(CLIENT_LOCAL_FILES, arg_) ?
				Option::err_NONE : Option::err_api_reject;
}

Option::Error
LocalInfileOption::set(DBDriver* dbd)
{
	return dbd->connected() ? Option::err_connected :
			dbd->set_option(MYSQL_OPT_LOCAL_INFILE, &arg_) ?
				Option::err_NONE : Option::err_api_reject;
}

Option::Error
MultiResultsOption::set(DBDriver* dbd)
{
#if MYSQL_VERSION_ID >= 40101
	if (dbd->connected()) {
		return dbd->set_option(arg_ ? MYSQL_OPTION_MULTI_STATEMENTS_ON :
				MYSQL_OPTION_MULTI_STATEMENTS_OFF) ?
				Option::err_NONE : Option::err_api_reject;
	}
	else {
		return dbd->set_option(CLIENT_MULTI_RESULTS, arg_) ?
				Option::err_NONE : Option::err_api_reject;
	}
#else
	return Option::err_api_limit;
#endif
}

Option::Error
MultiStatementsOption::set(DBDriver* dbd)
{
#if MYSQL_VERSION_ID >= 40101
	if (dbd->connected()) {
		return dbd->set_option(arg_ ? MYSQL_OPTION_MULTI_STATEMENTS_ON :
				MYSQL_OPTION_MULTI_STATEMENTS_OFF) ?
				Option::err_NONE : Option::err_api_reject;
	}
	else {
		return dbd->set_option(CLIENT_MULTI_STATEMENTS, arg_) ?
				Option::err_NONE : Option::err_api_reject;
	}
#else
	return Option::err_api_limit;
#endif
}

Option::Error
NamedPipeOption::set(DBDriver* dbd)
{
	return dbd->connected() ? Option::err_connected :
			dbd->set_option(MYSQL_OPT_NAMED_PIPE) ?
				Option::err_NONE : Option::err_api_reject;
}

Option::Error
NoSchemaOption::set(DBDriver* dbd)
{
	return dbd->connected() ? Option::err_connected :
			dbd->set_option(CLIENT_NO_SCHEMA, arg_) ?
				Option::err_NONE : Option::err_api_reject;
}

#if MYSQL_VERSION_ID > 40000		// only in 4.0 +
Option::Error
ProtocolOption::set(DBDriver* dbd)
{
	return dbd->connected() ? Option::err_connected :
			dbd->set_option(MYSQL_OPT_PROTOCOL, &arg_) ?
				Option::err_NONE : Option::err_api_reject;
}
#endif

Option::Error
ReadDefaultFileOption::set(DBDriver* dbd)
{
	return dbd->connected() ? Option::err_connected :
			dbd->set_option(MYSQL_READ_DEFAULT_FILE, arg_.c_str()) ?
				Option::err_NONE : Option::err_api_reject;
}

Option::Error
ReadDefaultGroupOption::set(DBDriver* dbd)
{
	return dbd->connected() ? Option::err_connected :
			dbd->set_option(MYSQL_READ_DEFAULT_GROUP, arg_.c_str()) ?
				Option::err_NONE : Option::err_api_reject;
}

Option::Error
ReadTimeoutOption::set(DBDriver* dbd)
{
#if MYSQL_VERSION_ID >= 40101
	return dbd->connected() ? Option::err_connected :
			dbd->set_option(MYSQL_OPT_READ_TIMEOUT, &arg_) ?
				Option::err_NONE : Option::err_api_reject;
#else
	return Option::err_api_limit;
#endif
}

Option::Error
ReconnectOption::set(DBDriver* dbd)
{
#if MYSQL_VERSION_ID >= 50013
	return dbd->connected() ? Option::err_connected :
			dbd->set_option(MYSQL_OPT_RECONNECT, &arg_) ?
				Option::err_NONE : Option::err_api_reject;
#else
	return Option::err_api_limit;
#endif
}

Option::Error
ReportDataTruncationOption::set(DBDriver* dbd)
{
#if MYSQL_VERSION_ID >= 50003
	return dbd->connected() ? Option::err_connected :
			dbd->set_option(MYSQL_REPORT_DATA_TRUNCATION, &arg_) ?
				Option::err_NONE : Option::err_api_reject;
#else
	return Option::err_api_limit;
#endif
}

Option::Error
SecureAuthOption::set(DBDriver* dbd)
{
#if MYSQL_VERSION_ID >= 40101
	return dbd->connected() ? Option::err_connected :
			dbd->set_option(MYSQL_SECURE_AUTH, &arg_) ?
				Option::err_NONE : Option::err_api_reject;
#else
	return Option::err_api_limit;
#endif
}

Option::Error
SetCharsetDirOption::set(DBDriver* dbd)
{
	return dbd->connected() ? Option::err_connected :
			dbd->set_option(MYSQL_SET_CHARSET_DIR, arg_.c_str()) ?
				Option::err_NONE : Option::err_api_reject;
}

Option::Error
SetCharsetNameOption::set(DBDriver* dbd)
{
	return dbd->connected() ? Option::err_connected :
			dbd->set_option(MYSQL_SET_CHARSET_NAME, arg_.c_str()) ?
				Option::err_NONE : Option::err_api_reject;
}

Option::Error
SetClientIpOption::set(DBDriver* dbd)
{
#if MYSQL_VERSION_ID >= 40101
	return dbd->connected() ? Option::err_connected :
			dbd->set_option(MYSQL_SET_CLIENT_IP, arg_.c_str()) ?
				Option::err_NONE : Option::err_api_reject;
#else
	return Option::err_api_limit;
#endif
}

Option::Error
SharedMemoryBaseNameOption::set(DBDriver* dbd)
{
#if MYSQL_VERSION_ID >= 40100
	return dbd->connected() ? Option::err_connected :
			dbd->set_option(MYSQL_SHARED_MEMORY_BASE_NAME, arg_.c_str()) ?
				Option::err_NONE : Option::err_api_reject;
#else
	return Option::err_api_limit;
#endif
}

Option::Error
SslOption::set(DBDriver* dbd)
{
#if defined(HAVE_MYSQL_SSL_SET)
	return dbd->connected() ? Option::err_connected :
			dbd->enable_ssl(
				key_.size() ? key_.c_str() : 0,
				cert_.size() ? cert_.c_str() : 0,
				ca_.size() ? ca_.c_str() : 0,
				capath_.size() ? capath_.c_str() : 0,
				cipher_.size() ? cipher_.c_str() : 0) ?
				Option::err_NONE : Option::err_api_reject;
#else
	return Option::err_api_limit;
#endif
}

Option::Error
UseEmbeddedConnectionOption::set(DBDriver* dbd)
{
#if MYSQL_VERSION_ID >= 40101
	return dbd->connected() ? Option::err_connected :
			dbd->set_option(MYSQL_OPT_USE_EMBEDDED_CONNECTION) ?
				Option::err_NONE : Option::err_api_reject;
#else
	return Option::err_api_limit;
#endif
}

Option::Error
UseRemoteConnectionOption::set(DBDriver* dbd)
{
#if MYSQL_VERSION_ID >= 40101
	return dbd->connected() ? Option::err_connected :
			dbd->set_option(MYSQL_OPT_USE_REMOTE_CONNECTION) ?
				Option::err_NONE : Option::err_api_reject;
#else
	return Option::err_api_limit;
#endif
}

Option::Error
WriteTimeoutOption::set(DBDriver* dbd)
{
#if MYSQL_VERSION_ID >= 40101
	return dbd->connected() ? Option::err_connected :
			dbd->set_option(MYSQL_OPT_WRITE_TIMEOUT, &arg_) ?
				Option::err_NONE : Option::err_api_reject;
#else
	return Option::err_api_limit;
#endif
}

#endif // !defined(DOXYGEN_IGNORE)

} // end namespace mysqlpp
<|endoftext|>


<commit_before>#ifndef GUARD_phatbooks_database_connection_hpp
#define GUARD_phatbooks_database_connection_hpp

/** \file phatbooks_database_connection.hpp
 *
 * \brief Header file pertaining to PhatbooksDatabaseConnection class.
 *
 * \author Matthew Harvey
 * \date 04 July 2012.
 *
 * Copyright (c) 2012, Matthew Harvey. All rights reserved.
 */

// TODO All the includes here thwart the compiler firewall
// that was one benefit of PIMPLing the business object classes.
// Think of a way around having to include the ..._impl classes.

#include "account_type.hpp"
#include "sqloxx/database_connection.hpp"
#include "sqloxx/general_typedefs.hpp"
#include <boost/bimap.hpp>
#include <boost/scoped_ptr.hpp>
#include <jewel/decimal.hpp>
#include <string>

namespace sqloxx
{
	// Forward declaration
	template <typename T, typename Connection>
	class IdentityMap;
}

namespace phatbooks
{

// Forward declarations
class BalanceCache;
class AccountImpl;
class CommodityImpl;
class EntryImpl;
class DraftJournalImpl;
class OrdinaryJournalImpl;
class RepeaterImpl;

/** Phatbooks-specific database connection class.
 * See API documentation for sqloxx::DatabaseConnection,
 * for parts of API inherited from sqloxx::DatabaseConnection.
 */
class PhatbooksDatabaseConnection:
	public sqloxx::DatabaseConnection
{
public:
	/**
	 * Exhibits the same throwing behaviour (if any) as
	 * default constructor for sqloxx::DatabaseConnection.
	 */
	PhatbooksDatabaseConnection();

	~PhatbooksDatabaseConnection();

	/**
	 * @returns \c true if and only if \c p_name is the name of an Account
	 * stored in the database.
	 *
	 * @param p_name name of Account.
	 *
	 * @throws SQLiteException or an exception derived therefrom, if
	 * something goes wrong with the SQL involved in executing
	 * the function. This might occur if \c p_name contains punctuation
	 * marks, spaces or etc., or is a SQL keyword, so that it cannot validly
	 * be a string parameter in a SQL statement. A SQLiteException or
	 * derivative might also be thrown if the database connection is invalid
	 * or the database corrupt. It is not guaranteed that these are the only
	 * circumstances in which an exception might be thrown.
	 */
	bool has_account_named(std::string const& p_name);

	/**
	 * @returns \c true if and only if \c p_name is the name of a
	 * DraftJournalImpl stored in the database.
	 *
	 * @todo Document throwing behaviour.
	 */
	bool has_draft_journal_named(std::string const& p_name);

	/**
	 * @returns a boost::bimap from AccountType to string, giving the
	 * string name for each AccountType.
	 *
	 * @todo Determine and document throwing behaviour.
	 */
	boost::bimap<account_type::AccountType, std::string> account_types();

	/**
	 * @returns \c true if and only if \c p_abbreviation is the abbreviation
	 * of a Commodity stored in the database.
	 */
	bool has_commodity_with_abbreviation(std::string const& p_abbreviation);

	/**
	 * @returns \c true if and only if \c p_name is the name of a Commodity
	 * stored in the database.
	 */
	bool has_commodity_named(std::string const& p_name);

	/**
	 * Creates tables required for Phatbooks, and inserts rows
	 * into certain tables to provide application-level data where
	 * required. If the database already contains these tables,
	 * the function does nothing. This function should always be
	 * called after calling DatabaseConnection::open.
	 *
	 * @throws SQLiteException or some derivative thereof, if setup is
	 * unsuccessful.
	 */
	void setup();

	/**
	 * Set degree of caching of objects loaded from database.
	 *
	 * Level 0 entails nil caching (except temporary caching required to
	 * avoid
	 * loading of duplicate objects).
	 *
	 * Level 5 entails some caching of objects of which there are only a few
	 * instances expected to exist in the database.
	 *
	 * Level 10 entails maximum caching.
	 *
	 * If \e level is not one of the specific levels listed above, the effect
	 * will be the same as the next lowest level that is listed above.
	 *
	 * When the caching level is changed from one significant level to another
	 * significant level that is lower than the first, any objects of classes
	 * that are not cached under the new level, that were cached under the old
	 * level, that are cached at the time the level changes, are emptied from
	 * the cache.
	 *
	 * @todo Determinate and document throwing behaviour.
	 */
	void set_caching_level(unsigned int level);

	/**
	 * Class to provide restricted access to cache holding
	 * Account balances.
	 */
	class BalanceCacheAttorney
	{
	public:
		friend class AccountImpl;
		friend class CommodityImpl;
		friend class EntryImpl;
	private:
		// Mark whole balance cache as stale
		static void mark_as_stale
		(	PhatbooksDatabaseConnection const& p_database_connection
		);
		// Mark a particular Account in the balance cache as stale
		static void mark_as_stale
		(	PhatbooksDatabaseConnection const& p_database_connection,
			sqloxx::Id p_account_id
		);
		// Retrieve the balance of an Account
		static jewel::Decimal balance
		(	PhatbooksDatabaseConnection const& p_database_connection,
			sqloxx::Id p_account_id
		);
	};

	friend class BalanceCacheAttorney;

	template<typename T>
	sqloxx::IdentityMap<T, PhatbooksDatabaseConnection>& identity_map();

private:

	bool setup_has_occurred();
	void mark_setup_as_having_occurred();

	BalanceCache* m_balance_cache;

	sqloxx::IdentityMap<AccountImpl, PhatbooksDatabaseConnection>*
		m_account_map;
	sqloxx::IdentityMap<CommodityImpl, PhatbooksDatabaseConnection>*
		m_commodity_map;
	sqloxx::IdentityMap<EntryImpl, PhatbooksDatabaseConnection>*
		m_entry_map;
	sqloxx::IdentityMap<OrdinaryJournalImpl, PhatbooksDatabaseConnection>*
		m_ordinary_journal_map;
	sqloxx::IdentityMap<DraftJournalImpl, PhatbooksDatabaseConnection>*
		m_draft_journal_map;
	sqloxx::IdentityMap<RepeaterImpl, PhatbooksDatabaseConnection>*
		m_repeater_map;

};  // PhatbooksDatabaseConnection

}  // namespace phatbooks

#endif  // GUARD_phatbooks_database_connection_hpp
<commit_msg>Updated a todo in phatbooks_database_connection.hpp.<commit_after>#ifndef GUARD_phatbooks_database_connection_hpp
#define GUARD_phatbooks_database_connection_hpp

/** \file phatbooks_database_connection.hpp
 *
 * \brief Header file pertaining to PhatbooksDatabaseConnection class.
 *
 * \author Matthew Harvey
 * \date 04 July 2012.
 *
 * Copyright (c) 2012, Matthew Harvey. All rights reserved.
 */

#include "account_type.hpp"
#include "sqloxx/database_connection.hpp"
#include "sqloxx/general_typedefs.hpp"
#include <boost/bimap.hpp>
#include <boost/scoped_ptr.hpp>
#include <jewel/decimal.hpp>
#include <string>

namespace sqloxx
{
	// Forward declaration
	template <typename T, typename Connection>
	class IdentityMap;
}

namespace phatbooks
{

// Forward declarations
class BalanceCache;
class AccountImpl;
class CommodityImpl;
class EntryImpl;
class DraftJournalImpl;
class OrdinaryJournalImpl;
class RepeaterImpl;

/** Phatbooks-specific database connection class.
 * See API documentation for sqloxx::DatabaseConnection,
 * for parts of API inherited from sqloxx::DatabaseConnection.
 */
class PhatbooksDatabaseConnection:
	public sqloxx::DatabaseConnection
{
public:
	/**
	 * Exhibits the same throwing behaviour (if any) as
	 * default constructor for sqloxx::DatabaseConnection.
	 */
	PhatbooksDatabaseConnection();

	~PhatbooksDatabaseConnection();

	/**
	 * @returns \c true if and only if \c p_name is the name of an Account
	 * stored in the database.
	 *
	 * @param p_name name of Account.
	 *
	 * @throws SQLiteException or an exception derived therefrom, if
	 * something goes wrong with the SQL involved in executing
	 * the function. This might occur if \c p_name contains punctuation
	 * marks, spaces or etc., or is a SQL keyword, so that it cannot validly
	 * be a string parameter in a SQL statement. A SQLiteException or
	 * derivative might also be thrown if the database connection is invalid
	 * or the database corrupt. It is not guaranteed that these are the only
	 * circumstances in which an exception might be thrown.
	 */
	bool has_account_named(std::string const& p_name);

	/**
	 * @returns \c true if and only if \c p_name is the name of a
	 * DraftJournalImpl stored in the database.
	 *
	 * @todo Document throwing behaviour.
	 */
	bool has_draft_journal_named(std::string const& p_name);

	/**
	 * @returns a boost::bimap from AccountType to string, giving the
	 * string name for each AccountType.
	 *
	 * @todo Determine and document throwing behaviour.
	 */
	boost::bimap<account_type::AccountType, std::string> account_types();

	/**
	 * @returns \c true if and only if \c p_abbreviation is the abbreviation
	 * of a Commodity stored in the database.
	 */
	bool has_commodity_with_abbreviation(std::string const& p_abbreviation);

	/**
	 * @returns \c true if and only if \c p_name is the name of a Commodity
	 * stored in the database.
	 */
	bool has_commodity_named(std::string const& p_name);

	/**
	 * Creates tables required for Phatbooks, and inserts rows
	 * into certain tables to provide application-level data where
	 * required. If the database already contains these tables,
	 * the function does nothing. This function should always be
	 * called after calling DatabaseConnection::open.
	 *
	 * @throws SQLiteException or some derivative thereof, if setup is
	 * unsuccessful.
	 */
	void setup();

	/**
	 * Set degree of caching of objects loaded from database.
	 *
	 * Level 0 entails nil caching (except temporary caching required to
	 * avoid
	 * loading of duplicate objects).
	 *
	 * Level 5 entails some caching of objects of which there are only a few
	 * instances expected to exist in the database.
	 *
	 * Level 10 entails maximum caching.
	 *
	 * If \e level is not one of the specific levels listed above, the effect
	 * will be the same as the next lowest level that is listed above.
	 *
	 * When the caching level is changed from one significant level to another
	 * significant level that is lower than the first, any objects of classes
	 * that are not cached under the new level, that were cached under the old
	 * level, that are cached at the time the level changes, are emptied from
	 * the cache.
	 *
	 * @todo Determinate and document throwing behaviour.
	 */
	void set_caching_level(unsigned int level);

	/**
	 * Class to provide restricted access to cache holding
	 * Account balances.
	 */
	class BalanceCacheAttorney
	{
	public:
		friend class AccountImpl;
		friend class CommodityImpl;
		friend class EntryImpl;
	private:
		// Mark whole balance cache as stale
		static void mark_as_stale
		(	PhatbooksDatabaseConnection const& p_database_connection
		);
		// Mark a particular Account in the balance cache as stale
		static void mark_as_stale
		(	PhatbooksDatabaseConnection const& p_database_connection,
			sqloxx::Id p_account_id
		);
		// Retrieve the balance of an Account
		static jewel::Decimal balance
		(	PhatbooksDatabaseConnection const& p_database_connection,
			sqloxx::Id p_account_id
		);
	};

	friend class BalanceCacheAttorney;

	template<typename T>
	sqloxx::IdentityMap<T, PhatbooksDatabaseConnection>& identity_map();

private:

	bool setup_has_occurred();
	void mark_setup_as_having_occurred();

	BalanceCache* m_balance_cache;

	sqloxx::IdentityMap<AccountImpl, PhatbooksDatabaseConnection>*
		m_account_map;
	sqloxx::IdentityMap<CommodityImpl, PhatbooksDatabaseConnection>*
		m_commodity_map;
	sqloxx::IdentityMap<EntryImpl, PhatbooksDatabaseConnection>*
		m_entry_map;
	sqloxx::IdentityMap<OrdinaryJournalImpl, PhatbooksDatabaseConnection>*
		m_ordinary_journal_map;
	sqloxx::IdentityMap<DraftJournalImpl, PhatbooksDatabaseConnection>*
		m_draft_journal_map;
	sqloxx::IdentityMap<RepeaterImpl, PhatbooksDatabaseConnection>*
		m_repeater_map;

};  // PhatbooksDatabaseConnection

}  // namespace phatbooks

#endif  // GUARD_phatbooks_database_connection_hpp
<|endoftext|>


<commit_before>// Copyright (c) 2018-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <chainparams.h>
#include <core_io.h>
#include <psbt.h>
#include <util/strencodings.h>
#include <util/system.h>
#include <external_signer.h>

#include <stdexcept>
#include <string>
#include <vector>

ExternalSigner::ExternalSigner(const std::string& command, const std::string chain, const std::string& fingerprint, const std::string name): m_command(command), m_chain(chain), m_fingerprint(fingerprint), m_name(name) {}

const std::string ExternalSigner::NetworkArg() const
{
    return " --chain " + m_chain;
}

bool ExternalSigner::Enumerate(const std::string& command, std::vector<ExternalSigner>& signers, const std::string chain)
{
    // Call <command> enumerate
    const UniValue result = RunCommandParseJSON(command + " enumerate");
    if (!result.isArray()) {
        throw std::runtime_error(strprintf("'%s' received invalid response, expected array of signers", command));
    }
    for (UniValue signer : result.getValues()) {
        // Check for error
        const UniValue& error = find_value(signer, "error");
        if (!error.isNull()) {
            if (!error.isStr()) {
                throw std::runtime_error(strprintf("'%s' error", command));
            }
            throw std::runtime_error(strprintf("'%s' error: %s", command, error.getValStr()));
        }
        // Check if fingerprint is present
        const UniValue& fingerprint = find_value(signer, "fingerprint");
        if (fingerprint.isNull()) {
            throw std::runtime_error(strprintf("'%s' received invalid response, missing signer fingerprint", command));
        }
        const std::string fingerprintStr = fingerprint.get_str();
        // Skip duplicate signer
        bool duplicate = false;
        for (const ExternalSigner& signer : signers) {
            if (signer.m_fingerprint.compare(fingerprintStr) == 0) duplicate = true;
        }
        if (duplicate) break;
        std::string name = "";
        const UniValue& model_field = find_value(signer, "model");
        if (model_field.isStr() && model_field.getValStr() != "") {
            name += model_field.getValStr();
        }
        signers.push_back(ExternalSigner(command, chain, fingerprintStr, name));
    }
    return true;
}

UniValue ExternalSigner::DisplayAddress(const std::string& descriptor) const
{
    return RunCommandParseJSON(m_command + " --fingerprint \"" + m_fingerprint + "\"" + NetworkArg() + " displayaddress --desc \"" + descriptor + "\"");
}

UniValue ExternalSigner::GetDescriptors(const int account)
{
    return RunCommandParseJSON(m_command + " --fingerprint \"" + m_fingerprint + "\"" + NetworkArg() + " getdescriptors --account " + strprintf("%d", account));
}

bool ExternalSigner::SignTransaction(PartiallySignedTransaction& psbtx, std::string& error)
{
    // Serialize the PSBT
    CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
    ssTx << psbtx;

    // Check if signer fingerprint matches any input master key fingerprint
    bool match = false;
    for (unsigned int i = 0; i < psbtx.inputs.size(); ++i) {
        const PSBTInput& input = psbtx.inputs[i];
        for (const auto& entry : input.hd_keypaths) {
            if (m_fingerprint == strprintf("%08x", ReadBE32(entry.second.fingerprint))) match = true;
        }
    }

    if (!match) {
        error = "Signer fingerprint " + m_fingerprint + " does not match any of the inputs:\n" + EncodeBase64(ssTx.str());
        return false;
    }

    const std::string command = m_command + " --stdin --fingerprint \"" + m_fingerprint + "\"" + NetworkArg();
    const std::string stdinStr = "signtx \"" + EncodeBase64(ssTx.str()) + "\"";

    const UniValue signer_result = RunCommandParseJSON(command, stdinStr);

    if (find_value(signer_result, "error").isStr()) {
        error = find_value(signer_result, "error").get_str();
        return false;
    }

    if (!find_value(signer_result, "psbt").isStr()) {
        error = "Unexpected result from signer";
        return false;
    }

    PartiallySignedTransaction signer_psbtx;
    std::string signer_psbt_error;
    if (!DecodeBase64PSBT(signer_psbtx, find_value(signer_result, "psbt").get_str(), signer_psbt_error)) {
        error = strprintf("TX decode failed %s", signer_psbt_error);
        return false;
    }

    psbtx = signer_psbtx;

    return true;
}
<commit_msg>external_signer: improve fingerprint matching logic (stop on first match)<commit_after>// Copyright (c) 2018-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <chainparams.h>
#include <core_io.h>
#include <psbt.h>
#include <util/strencodings.h>
#include <util/system.h>
#include <external_signer.h>

#include <algorithm>
#include <stdexcept>
#include <string>
#include <vector>

ExternalSigner::ExternalSigner(const std::string& command, const std::string chain, const std::string& fingerprint, const std::string name): m_command(command), m_chain(chain), m_fingerprint(fingerprint), m_name(name) {}

const std::string ExternalSigner::NetworkArg() const
{
    return " --chain " + m_chain;
}

bool ExternalSigner::Enumerate(const std::string& command, std::vector<ExternalSigner>& signers, const std::string chain)
{
    // Call <command> enumerate
    const UniValue result = RunCommandParseJSON(command + " enumerate");
    if (!result.isArray()) {
        throw std::runtime_error(strprintf("'%s' received invalid response, expected array of signers", command));
    }
    for (UniValue signer : result.getValues()) {
        // Check for error
        const UniValue& error = find_value(signer, "error");
        if (!error.isNull()) {
            if (!error.isStr()) {
                throw std::runtime_error(strprintf("'%s' error", command));
            }
            throw std::runtime_error(strprintf("'%s' error: %s", command, error.getValStr()));
        }
        // Check if fingerprint is present
        const UniValue& fingerprint = find_value(signer, "fingerprint");
        if (fingerprint.isNull()) {
            throw std::runtime_error(strprintf("'%s' received invalid response, missing signer fingerprint", command));
        }
        const std::string fingerprintStr = fingerprint.get_str();
        // Skip duplicate signer
        bool duplicate = false;
        for (const ExternalSigner& signer : signers) {
            if (signer.m_fingerprint.compare(fingerprintStr) == 0) duplicate = true;
        }
        if (duplicate) break;
        std::string name = "";
        const UniValue& model_field = find_value(signer, "model");
        if (model_field.isStr() && model_field.getValStr() != "") {
            name += model_field.getValStr();
        }
        signers.push_back(ExternalSigner(command, chain, fingerprintStr, name));
    }
    return true;
}

UniValue ExternalSigner::DisplayAddress(const std::string& descriptor) const
{
    return RunCommandParseJSON(m_command + " --fingerprint \"" + m_fingerprint + "\"" + NetworkArg() + " displayaddress --desc \"" + descriptor + "\"");
}

UniValue ExternalSigner::GetDescriptors(const int account)
{
    return RunCommandParseJSON(m_command + " --fingerprint \"" + m_fingerprint + "\"" + NetworkArg() + " getdescriptors --account " + strprintf("%d", account));
}

bool ExternalSigner::SignTransaction(PartiallySignedTransaction& psbtx, std::string& error)
{
    // Serialize the PSBT
    CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
    ssTx << psbtx;

    // Check if signer fingerprint matches any input master key fingerprint
    auto matches_signer_fingerprint = [&](const PSBTInput& input) {
        for (const auto& entry : input.hd_keypaths) {
            if (m_fingerprint == strprintf("%08x", ReadBE32(entry.second.fingerprint))) return true;
        }
        return false;
    };

    if (!std::any_of(psbtx.inputs.begin(), psbtx.inputs.end(), matches_signer_fingerprint)) {
        error = "Signer fingerprint " + m_fingerprint + " does not match any of the inputs:\n" + EncodeBase64(ssTx.str());
        return false;
    }

    const std::string command = m_command + " --stdin --fingerprint \"" + m_fingerprint + "\"" + NetworkArg();
    const std::string stdinStr = "signtx \"" + EncodeBase64(ssTx.str()) + "\"";

    const UniValue signer_result = RunCommandParseJSON(command, stdinStr);

    if (find_value(signer_result, "error").isStr()) {
        error = find_value(signer_result, "error").get_str();
        return false;
    }

    if (!find_value(signer_result, "psbt").isStr()) {
        error = "Unexpected result from signer";
        return false;
    }

    PartiallySignedTransaction signer_psbtx;
    std::string signer_psbt_error;
    if (!DecodeBase64PSBT(signer_psbtx, find_value(signer_result, "psbt").get_str(), signer_psbt_error)) {
        error = strprintf("TX decode failed %s", signer_psbt_error);
        return false;
    }

    psbtx = signer_psbtx;

    return true;
}
<|endoftext|>