<commit_before>#pragma once

#include <functional>
#include <gcl_cpp/type_info.hpp>

namespace gcl::functionnal
{
	template <class F>
	class trait
	{	// todo : multiple operator()
		using base_type = std::decay_t<F>;
		using base_operator_type = decltype(&base_type::operator());

		template <typename return_t, typename ... args_t>
		static constexpr auto deduce(return_t(base_type::*)(args_t...) const)
		{
			struct deduce_t
			{
				constexpr deduce_t() = default;
				using return_type = return_t;
				using arguments_type = gcl::type_info::pack<args_t...>;
			};
			return deduce_t{};
		}

		using deducer = decltype(deduce(&base_type::operator()));

	public:
		using return_type = typename deducer::return_type;
		using arguments_type = typename deducer::arguments_type;
	};

	//template <typename T, typename = void>
	//struct is_callable : std::false_type {};
	//template <typename T>
	//struct is_callable<T, std::void_t<decltype(std::declval<T>().operator())>> : std::true_type {}; // std::decay_t

	template <typename ... Fs, typename ... args_t>
	auto combine_impl(gcl::type_info::pack<args_t...>, Fs && ... funcs)
	{
		if constexpr (sizeof...(args_t) == 0)
			return [=]()
		{
			[[maybe_unused]] int _[] = { (funcs(), 0)... };
		};
		else
			return [=](args_t ... args)
		{
			[[maybe_unused]] int _[] = { (funcs(args...), 0)... };
		};
	}
	template <typename ... Fs>
	auto combine(Fs && ... funcs)
	{
		using type = typename std::tuple_element<0, std::tuple<Fs...>>::type;
		using trait = trait<type>;

		// todo : static_assert(is_all<...>, "failed to combine heterogenous functions types");

		return combine_impl(typename trait::arguments_type{}, std::forward<Fs>(funcs)...);
	}
	template <>
	auto combine() { return []() {}; }

	namespace deprecated
	{
		struct finally : public std::function<void()>
		{
			explicit finally(std::function<void()> && func)
			{
				func.swap(*this);
			}
			finally(const finally & w)
			{
				const_cast<finally &>(w).swap(*this);
			}
			~finally()
			{
				if (*this)
					(*this)();
			}
		};
	}
}

template
<
	class F1, class F2,
	typename std::enable_if_t
	<
		std::is_invocable_v<F1> &&
		std::is_invocable_v<F2>
		, int
	> = 0
>
static auto operator+(F1 && f1, F2 && f2)
{
	return gcl::functionnal::combine(std::forward<F1>(f1), std::forward<F2>(f2));
}

<commit_msg>gcl::functionnal : improve gcl::functionnal::combine to add homogeneous and hetegeneous combination<commit_after>#pragma once

#include <functional>
#include <gcl_cpp/type_info.hpp>

namespace gcl::functionnal
{
	template <class F>
	class trait
	{	// todo : multiple operator()
		using base_type = std::decay_t<F>;
		using base_operator_type = decltype(&base_type::operator());

		template <typename return_t, typename ... args_t>
		static constexpr auto deduce(return_t(base_type::*)(args_t...) const)
		{
			struct deduce_t
			{
				constexpr deduce_t() = default;
				using return_type = return_t;
				using arguments_type = gcl::type_info::pack<args_t...>;
			};
			return deduce_t{};
		}

		using deducer = decltype(deduce(&base_type::operator()));

	public:
		using return_type = typename deducer::return_type;
		using arguments_type = typename deducer::arguments_type;
	};

	//template <typename T, typename = void>
	//struct is_callable : std::false_type {};
	//template <typename T>
	//struct is_callable<T, std::void_t<decltype(std::declval<T>().operator())>> : std::true_type {}; // std::decay_t

	template <typename ... Fs, typename ... args_t>
	auto combine_homogeneous_impl(gcl::type_info::pack<args_t...>, Fs && ... funcs)
	{
		if constexpr (sizeof...(args_t) == 0)
			return [=]()
		{
			[[maybe_unused]] int _[] = { (funcs(), 0)... };
		};
		else
			return [=](args_t ... args)
		{
			[[maybe_unused]] int _[] = { (funcs(args...), 0)... };
		};
	}
	template <typename ... Fs>
	auto combine_homogeneous(Fs && ... funcs)
	{
		using type = typename std::tuple_element<0, std::tuple<Fs...>>::type;
		using trait = trait<type>;

		// todo : static_assert(is_all<...>, "failed to combine heterogenous functions types");
		//        or constexpr if that call combine_heterogeneous

		return combine_homogeneous_impl(typename trait::arguments_type{}, std::forward<Fs>(funcs)...);
	}
	template <>
	auto combine_homogeneous() { return []() {}; }

	template <typename ... Fs>
	auto combine_heterogeneous(Fs ... funcs)
	{
		struct adapter : std::decay_t<Fs>...
		{
			adapter(std::decay_t<Fs> ... values)
				: std::decay_t<Fs>(std::move(values))...
			{}

			using Fs::operator()...;
		};
		return adapter{ std::forward<Fs>(funcs)... };
	}

	// todo : combine(Fs ... funcs)

	namespace deprecated
	{
		struct finally : public std::function<void()>
		{
			explicit finally(std::function<void()> && func)
			{
				func.swap(*this);
			}
			finally(const finally & w)
			{
				const_cast<finally &>(w).swap(*this);
			}
			~finally()
			{
				if (*this)
					(*this)();
			}
		};
	}
}

template
<
	class F1, class F2,
	typename std::enable_if_t
	<
		std::is_invocable_v<F1> &&
		std::is_invocable_v<F2>
		, int
	> = 0
>
static auto operator+(F1 && f1, F2 && f2)
{
	return gcl::functionnal::combine_homogeneous(std::forward<F1>(f1), std::forward<F2>(f2));
}

<|endoftext|>


<commit_before>#ifndef CCHRONO_TIMER
#define CCHRONO_TIMER
#include<chrono>

namespace nTool
{
	class CChrono_timer
	{
		std::chrono::high_resolution_clock::time_point begin_;
		std::chrono::high_resolution_clock::time_point end_;
	public:
		constexpr std::chrono::hours::rep duration_hours() const
		{
			return std::chrono::duration_cast<std::chrono::hours>(end_-begin_).count();
		}
		constexpr std::chrono::minutes::rep duration_minutes() const
		{
			return std::chrono::duration_cast<std::chrono::minutes>(end_-begin_).count();
		}
		constexpr std::chrono::seconds::rep duration_seconds() const
		{
			return std::chrono::duration_cast<std::chrono::seconds>(end_-begin_).count();
		}
		constexpr std::chrono::milliseconds::rep duration_milliseconds() const
		{
			return std::chrono::duration_cast<std::chrono::milliseconds>(end_-begin_).count();
		}
		constexpr std::chrono::microseconds::rep duration_microseconds() const
		{
			return std::chrono::duration_cast<std::chrono::microseconds>(end_-begin_).count();
		}
		constexpr std::chrono::nanoseconds::rep duration_nanoseconds() const
		{
			return std::chrono::duration_cast<std::chrono::nanoseconds>(end_-begin_).count();
		}
		inline void start() noexcept
		{
			begin_=std::chrono::high_resolution_clock::now();
		}
		inline void stop() noexcept
		{
			end_=std::chrono::high_resolution_clock::now();
		}
	};
}

#endif<commit_msg>add calc_time<commit_after>#ifndef CCHRONO_TIMER
#define CCHRONO_TIMER
#include<chrono>

namespace nTool
{
	class CChrono_timer
	{
		std::chrono::high_resolution_clock::time_point begin_;
		std::chrono::high_resolution_clock::time_point end_;
	public:
		constexpr std::chrono::hours::rep duration_hours() const
		{
			return std::chrono::duration_cast<std::chrono::hours>(end_-begin_).count();
		}
		constexpr std::chrono::minutes::rep duration_minutes() const
		{
			return std::chrono::duration_cast<std::chrono::minutes>(end_-begin_).count();
		}
		constexpr std::chrono::seconds::rep duration_seconds() const
		{
			return std::chrono::duration_cast<std::chrono::seconds>(end_-begin_).count();
		}
		constexpr std::chrono::milliseconds::rep duration_milliseconds() const
		{
			return std::chrono::duration_cast<std::chrono::milliseconds>(end_-begin_).count();
		}
		constexpr std::chrono::microseconds::rep duration_microseconds() const
		{
			return std::chrono::duration_cast<std::chrono::microseconds>(end_-begin_).count();
		}
		constexpr std::chrono::nanoseconds::rep duration_nanoseconds() const
		{
			return std::chrono::duration_cast<std::chrono::nanoseconds>(end_-begin_).count();
		}
		inline void start() noexcept
		{
			begin_=std::chrono::high_resolution_clock::now();
		}
		inline void stop() noexcept
		{
			end_=std::chrono::high_resolution_clock::now();
		}
	};

	template<class Func,class ... Args>
	CChrono_timer calc_time(const Func func,Args &&...args)
	{
		CChrono_timer timer;
		timer.start();
		func(std::forward<Args>(args)...);
		timer.stop();
		return timer;
	}
}

#endif<|endoftext|>


<commit_before>#include <mill/util/logger.hpp>
#include <mill/util/file_extension.hpp>
#include <mill/traj.hpp>
#include "mode_traj_projection.hpp"
#include <iostream>

namespace mill
{

const char* mode_traj_projection_usage() noexcept
{
    return "usage: mill traj projection [trajfile] [axes] [origin]\n"
           "       project trajectory onto a 3N-dimensional vector (axes).\n"
           "       To pass a 3N dimensional vector, trajectory file can be used.\n"
           "       The file should have the same number of elements as the traj file.\n";
}

int mode_traj_projection(std::deque<std::string_view> args)
{
    using namespace std::literals::string_view_literals;
    using namespace std::literals::string_literals;
    if(args.empty())
    {
        log::error("mill traj projection: too few arguments");
        log::error(mode_traj_projection_usage());
        return 1;
    }

    const auto traj = args.front(); args.pop_front();

    if(traj == "help")
    {
        log::info(mode_traj_projection_usage());
        return 0;
    }
    else if(args.size() < 2)
    {
        log::error("mill traj projection: too few arguments");
        log::info(mode_traj_projection_usage());
        return 1;
    }

    const auto axes_file = args.front(); args.pop_front();
    const auto orig_file = args.front(); args.pop_front();

    const auto origin_traj = reader(orig_file).read();
    const auto axes        = reader(axes_file).read();

    if(1 < origin_traj.size())
    {
        log::warn("mill traj projection: origin file (", orig_file, ") has ",
                  "multiple (", origin_traj.size(), ") number of structures. Only "
                  "the first frame is used as the origin.");
        return 1;
    }
    const auto origin = origin_traj.at(0);

    const auto output = std::string(base_name_of(traj)) + "_projected_"s +
        std::string(extension_of(traj));

    std::ofstream ofs(output);
    if(!ofs.good())
    {
        log::error("mill traj projection: file open error -> ", output);
        return 1;
    }

    ofs << "# [frame index], [coordinate along 1st axis], [coordinate along 2nd axis], ...\n";
    std::size_t t = 0;
    for(auto frame : reader(traj))
    {
        ofs << std::setw(8) << std::right << t ;
        for(const auto& axis : axes)
        {
            if(frame.size() != axis.size())
            {
                log::fatal("mill traj projection: number of particles in a frame"
                           " differs from that of the axis");
            }
            if(frame.size() != origin.size())
            {
                log::fatal("mill traj projection: number of particles in a frame"
                           " differs from that of the origin");
            }

            double coord = 0;
            for(std::size_t i=0; i<frame.size(); ++i)
            {
                coord += mill::dot_product(frame.at(i).position() - origin.at(i).position(), axis.at(i).position());
            }
            ofs << ' ' << std::setprecision(10) << coord << std::endl;
        }
        ofs << '\n';
    }
    return 0;
}

} // mill
<commit_msg>:bug: fix output format<commit_after>#include <mill/util/logger.hpp>
#include <mill/util/file_extension.hpp>
#include <mill/traj.hpp>
#include "mode_traj_projection.hpp"
#include <iostream>

namespace mill
{

const char* mode_traj_projection_usage() noexcept
{
    return "usage: mill traj projection [trajfile] [axes] [origin]\n"
           "       project trajectory onto a 3N-dimensional vector (axes).\n"
           "       To pass a 3N dimensional vector, trajectory file can be used.\n"
           "       The file should have the same number of elements as the traj file.\n";
}

int mode_traj_projection(std::deque<std::string_view> args)
{
    using namespace std::literals::string_view_literals;
    using namespace std::literals::string_literals;
    if(args.empty())
    {
        log::error("mill traj projection: too few arguments");
        log::error(mode_traj_projection_usage());
        return 1;
    }

    const auto traj = args.front(); args.pop_front();

    if(traj == "help")
    {
        log::info(mode_traj_projection_usage());
        return 0;
    }
    else if(args.size() < 2)
    {
        log::error("mill traj projection: too few arguments");
        log::info(mode_traj_projection_usage());
        return 1;
    }

    const auto axes_file = args.front(); args.pop_front();
    const auto orig_file = args.front(); args.pop_front();

    const auto origin_traj = reader(orig_file).read();
    const auto axes        = reader(axes_file).read();

    if(1 < origin_traj.size())
    {
        log::warn("mill traj projection: origin file (", orig_file, ") has ",
                  "multiple (", origin_traj.size(), ") number of structures. Only "
                  "the first frame is used as the origin.");
        return 1;
    }
    const auto origin = origin_traj.at(0);

    const auto output = std::string(base_name_of(traj)) + "_projected_"s +
        std::string(extension_of(traj));

    std::ofstream ofs(output);
    if(!ofs.good())
    {
        log::error("mill traj projection: file open error -> ", output);
        return 1;
    }

    ofs << "# [frame index], [coordinate along 1st axis], [coordinate along 2nd axis], ...\n";
    std::size_t t = 0;
    for(auto frame : reader(traj))
    {
        ofs << std::setw(8) << std::right << t ;
        for(const auto& axis : axes)
        {
            if(frame.size() != axis.size())
            {
                log::fatal("mill traj projection: number of particles in a frame"
                           " differs from that of the axis");
            }
            if(frame.size() != origin.size())
            {
                log::fatal("mill traj projection: number of particles in a frame"
                           " differs from that of the origin");
            }

            double coord = 0;
            for(std::size_t i=0; i<frame.size(); ++i)
            {
                coord += mill::dot_product(frame.at(i).position() - origin.at(i).position(), axis.at(i).position());
            }
            ofs << ' ' << std::setprecision(10) << coord;
        }
        ofs << '\n';
    }
    return 0;
}

} // mill
<|endoftext|>


<commit_before>// The MIT License(MIT)
// Copyright(c) 2016  Jeff Rebacz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files(the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and / or sell copies
// of the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions :
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

#ifndef COLORWIN_HPP_INCLUDED
#define COLORWIN_HPP_INCLUDED

#include <Windows.h>
#include <iostream>
#include <stack>

namespace colorwin
{
    // mix colors from wincon.h
    //#define FOREGROUND_BLUE      0x0001 // text color contains blue.
    //#define FOREGROUND_GREEN     0x0002 // text color contains green.
    //#define FOREGROUND_RED       0x0004 // text color contains red.
    //#define FOREGROUND_INTENSITY 0x0008 // text color is intensified.
    enum CW_COLORS
    {
        red = FOREGROUND_RED | FOREGROUND_INTENSITY,
        yellow = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY,
        green = FOREGROUND_GREEN | FOREGROUND_INTENSITY,
        cyan = FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY,
        blue = FOREGROUND_BLUE | FOREGROUND_INTENSITY,
        magenta = FOREGROUND_BLUE | FOREGROUND_RED | FOREGROUND_INTENSITY,
        white = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY,
        gray = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE,
        grey = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE,

        dark_gray = FOREGROUND_INTENSITY,
        dark_grey = FOREGROUND_INTENSITY,

        /* The following dark colors are unreadable, but comment them in and use them if you want. */
        /*
        dark_red = FOREGROUND_RED,
        dark_yellow = FOREGROUND_RED | FOREGROUND_GREEN,
        dark_green = FOREGROUND_GREEN,
        dark_cyan = FOREGROUND_GREEN | FOREGROUND_BLUE,
        dark_blue = FOREGROUND_BLUE,
        dark_magenta = FOREGROUND_BLUE | FOREGROUND_RED,
        */
    };

    // Example usage: std::cout << color(yellow) << "This is a warning color!\n";
    class color
    {
    public:
        color(CW_COLORS color) : m_color(color), m_console_handle(INVALID_HANDLE_VALUE)
        {
            CONSOLE_SCREEN_BUFFER_INFO console_info;
            m_console_handle = GetStdHandle(STD_OUTPUT_HANDLE);
            if (!GetConsoleScreenBufferInfo(m_console_handle, &console_info))
            {
                m_console_handle = GetStdHandle(STD_ERROR_HANDLE);
                if (!GetConsoleScreenBufferInfo(m_console_handle, &console_info)) // maybe standard output device has been redirected, try the standard error device
                {
                    m_console_handle = INVALID_HANDLE_VALUE;
                }
            }
        }

        ~color()
        {
            if (m_console_handle != INVALID_HANDLE_VALUE)
            {
                // Restore the previous color.
                SetConsoleTextAttribute(m_console_handle, get_color_stack().top());
                get_color_stack().pop();
            }
        }

    private:
        void change_color() const
        {
            if (m_console_handle == INVALID_HANDLE_VALUE)
                return; // Can't get console info, can't change color.
            CONSOLE_SCREEN_BUFFER_INFO console_info;
            GetConsoleScreenBufferInfo(m_console_handle, &console_info);
            // save the current attributes for restoration on destruction.
            get_color_stack().push(console_info.wAttributes);
            SetConsoleTextAttribute(m_console_handle, 0x0F & m_color);
        }

        color(color &);
        color& operator=(color);

        static std::stack<WORD>& get_color_stack()
        {
            // Use this instead of static member to avoid multiply defined symbols.
            static std::stack<WORD> color_stack;
            return color_stack;
        }

        HANDLE m_console_handle;
        const CW_COLORS m_color;

        friend class withcolor;
        template<typename charT, typename traits> friend std::basic_ostream<charT, traits> & operator<<(std::basic_ostream<charT, traits> &lhs, colorwin::color const &rhs);
    };

    // Example usage :
    //  {
    //      withcolor scoped(yellow);
    //      cout << "This is a yellow warning!\n";
    //      cout << "This is a second yellow warning!\n";
    //  }
    //  --  or  --
    //      withcolor(yellow).printf("This will be yellow\n");
    class withcolor
    {
    public:
        withcolor(CW_COLORS color) : m_color(color)
        {
            m_color.change_color();
        }

        int printf(const char* format, ...)
        {
            va_list vlist;
            va_start(vlist, format);
            int ret = vprintf(format, vlist);
            va_end(vlist);
            return ret;
        }

        #if _MSC_VER >= 1400    // printf_s offered in Visual Studio 2005
        int printf_s(const char *format, ...)
        {
            va_list vlist;
            va_start(vlist, format);
            int ret = vprintf_s(format, vlist);
            va_end(vlist);
            return ret;
        }
        #endif

    private:
        withcolor(withcolor &);
        withcolor& operator=(withcolor);

        color m_color;
    };

    // cout << color(red) -> operator<<(cout, colorwin::color(red))
    template<typename charT, typename traits> std::basic_ostream<charT, traits> & operator<<(std::basic_ostream<charT, traits> &lhs, colorwin::color const &rhs)
    {
        rhs.change_color();
        return lhs;
    }
}

#endif // COLORWIN_HPP_INCLUDED<commit_msg>When applying the color attribute, include the previous background color.  Noticed that a black background was appearing in the sample code when it ran from powershell (blue background turning into a black background).<commit_after>// The MIT License(MIT)
// Copyright(c) 2016  Jeff Rebacz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files(the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and / or sell copies
// of the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions :
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

#ifndef COLORWIN_HPP_INCLUDED
#define COLORWIN_HPP_INCLUDED

#include <Windows.h>
#include <iostream>
#include <stack>

namespace colorwin
{
    // mix colors from wincon.h
    //#define FOREGROUND_BLUE      0x0001 // text color contains blue.
    //#define FOREGROUND_GREEN     0x0002 // text color contains green.
    //#define FOREGROUND_RED       0x0004 // text color contains red.
    //#define FOREGROUND_INTENSITY 0x0008 // text color is intensified.
    enum CW_COLORS
    {
        red = FOREGROUND_RED | FOREGROUND_INTENSITY,
        yellow = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY,
        green = FOREGROUND_GREEN | FOREGROUND_INTENSITY,
        cyan = FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY,
        blue = FOREGROUND_BLUE | FOREGROUND_INTENSITY,
        magenta = FOREGROUND_BLUE | FOREGROUND_RED | FOREGROUND_INTENSITY,
        white = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY,
        gray = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE,
        grey = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE,

        dark_gray = FOREGROUND_INTENSITY,
        dark_grey = FOREGROUND_INTENSITY,

        /* The following dark colors are unreadable, but comment them in and use them if you want. */
        /*
        dark_red = FOREGROUND_RED,
        dark_yellow = FOREGROUND_RED | FOREGROUND_GREEN,
        dark_green = FOREGROUND_GREEN,
        dark_cyan = FOREGROUND_GREEN | FOREGROUND_BLUE,
        dark_blue = FOREGROUND_BLUE,
        dark_magenta = FOREGROUND_BLUE | FOREGROUND_RED,
        */
    };

    // Example usage: std::cout << color(yellow) << "This is a warning color!\n";
    class color
    {
    public:
        color(CW_COLORS color) : m_color(color), m_console_handle(INVALID_HANDLE_VALUE)
        {
            CONSOLE_SCREEN_BUFFER_INFO console_info;
            m_console_handle = GetStdHandle(STD_OUTPUT_HANDLE);
            if (!GetConsoleScreenBufferInfo(m_console_handle, &console_info))
            {
                m_console_handle = GetStdHandle(STD_ERROR_HANDLE);
                if (!GetConsoleScreenBufferInfo(m_console_handle, &console_info)) // maybe standard output device has been redirected, try the standard error device
                {
                    m_console_handle = INVALID_HANDLE_VALUE;
                }
            }
        }

        ~color()
        {
            if (m_console_handle != INVALID_HANDLE_VALUE)
            {
                // Restore the previous color.
                SetConsoleTextAttribute(m_console_handle, get_color_stack().top());
                get_color_stack().pop();
            }
        }

    private:
        void change_color() const
        {
            if (m_console_handle == INVALID_HANDLE_VALUE)
                return; // Can't get console info, can't change color.
            CONSOLE_SCREEN_BUFFER_INFO console_info;
            GetConsoleScreenBufferInfo(m_console_handle, &console_info);
            // save the current attributes for restoration on destruction.
            get_color_stack().push(console_info.wAttributes);
            SetConsoleTextAttribute(m_console_handle, 0x0F & m_color | 0xf0 & console_info.wAttributes);    // save the background color
        }

        color(color &);
        color& operator=(color);

        static std::stack<WORD>& get_color_stack()
        {
            // Use this instead of static member to avoid multiply defined symbols.
            static std::stack<WORD> color_stack;
            return color_stack;
        }

        HANDLE m_console_handle;
        const CW_COLORS m_color;

        friend class withcolor;
        template<typename charT, typename traits> friend std::basic_ostream<charT, traits> & operator<<(std::basic_ostream<charT, traits> &lhs, colorwin::color const &rhs);
    };

    // Example usage :
    //  {
    //      withcolor scoped(yellow);
    //      cout << "This is a yellow warning!\n";
    //      cout << "This is a second yellow warning!\n";
    //  }
    //  --  or  --
    //      withcolor(yellow).printf("This will be yellow\n");
    class withcolor
    {
    public:
        withcolor(CW_COLORS color) : m_color(color)
        {
            m_color.change_color();
        }

        int printf(const char* format, ...)
        {
            va_list vlist;
            va_start(vlist, format);
            int ret = vprintf(format, vlist);
            va_end(vlist);
            return ret;
        }

        #if _MSC_VER >= 1400    // printf_s offered in Visual Studio 2005
        int printf_s(const char *format, ...)
        {
            va_list vlist;
            va_start(vlist, format);
            int ret = vprintf_s(format, vlist);
            va_end(vlist);
            return ret;
        }
        #endif

    private:
        withcolor(withcolor &);
        withcolor& operator=(withcolor);

        color m_color;
    };

    // cout << color(red) -> operator<<(cout, colorwin::color(red))
    template<typename charT, typename traits> std::basic_ostream<charT, traits> & operator<<(std::basic_ostream<charT, traits> &lhs, colorwin::color const &rhs)
    {
        rhs.change_color();
        return lhs;
    }
}

#endif // COLORWIN_HPP_INCLUDED<|endoftext|>


<commit_before>// Copyright (c) 2011-2012 Ryan Prichard
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.

#include <string.h>

#include <algorithm>

#include "EventLoop.h"
#include "NamedPipe.h"
#include "../shared/DebugClient.h"
#include "../shared/StringUtil.h"
#include "../shared/WindowsSecurity.h"
#include "../shared/WinptyAssert.h"

// Returns true if anything happens (data received, data sent, pipe error).
bool NamedPipe::serviceIo(std::vector<HANDLE> *waitHandles)
{
    bool justConnected = false;
    const auto kError = ServiceResult::Error;
    const auto kProgress = ServiceResult::Progress;
    const auto kNoProgress = ServiceResult::NoProgress;
    if (m_handle == NULL) {
        return false;
    }
    if (m_connectEvent.get() != nullptr) {
        // We're still connecting this server pipe.  Check whether the pipe is
        // now connected.  If it isn't, add the pipe to the list of handles to
        // wait on.
        DWORD actual = 0;
        BOOL success =
            GetOverlappedResult(m_handle, &m_connectOver, &actual, FALSE);
        if (!success && GetLastError() == ERROR_PIPE_CONNECTED) {
            // I'm not sure this can happen, but it's easy to handle if it
            // does.
            success = TRUE;
        }
        if (!success) {
            ASSERT(GetLastError() == ERROR_IO_INCOMPLETE &&
                "Pended ConnectNamedPipe call failed");
            waitHandles->push_back(m_connectEvent.get());
        } else {
            TRACE("Server pipe [%s] connected",
                utf8FromWide(m_name).c_str());
            m_connectEvent.dispose();
            startPipeWorkers();
            justConnected = true;
        }
    }
    const auto readProgress = m_inputWorker ? m_inputWorker->service() : kNoProgress;
    const auto writeProgress = m_outputWorker ? m_outputWorker->service() : kNoProgress;
    if (readProgress == kError || writeProgress == kError) {
        closePipe();
        return true;
    }
    if (m_inputWorker && m_inputWorker->getWaitEvent() != nullptr) {
        waitHandles->push_back(m_inputWorker->getWaitEvent());
    }
    if (m_outputWorker && m_outputWorker->getWaitEvent() != nullptr) {
        waitHandles->push_back(m_outputWorker->getWaitEvent());
    }
    return justConnected
        || readProgress == kProgress
        || writeProgress == kProgress;
}

// manual reset, initially unset
static OwnedHandle createEvent() {
    HANDLE ret = CreateEventW(nullptr, TRUE, FALSE, nullptr);
    ASSERT(ret != nullptr && "CreateEventW failed");
    return OwnedHandle(ret);
}

NamedPipe::IoWorker::IoWorker(NamedPipe &namedPipe) :
    m_namedPipe(namedPipe),
    m_event(createEvent())
{
}

NamedPipe::ServiceResult NamedPipe::IoWorker::service()
{
    ServiceResult progress = ServiceResult::NoProgress;
    if (m_pending) {
        DWORD actual = 0;
        BOOL ret = GetOverlappedResult(m_namedPipe.m_handle, &m_over, &actual, FALSE);
        if (!ret) {
            if (GetLastError() == ERROR_IO_INCOMPLETE) {
                // There is a pending I/O.
                return progress;
            } else {
                // Pipe error.
                return ServiceResult::Error;
            }
        }
        ResetEvent(m_event.get());
        m_pending = false;
        completeIo(actual);
        m_currentIoSize = 0;
        progress = ServiceResult::Progress;
    }
    DWORD nextSize = 0;
    bool isRead = false;
    while (shouldIssueIo(&nextSize, &isRead)) {
        m_currentIoSize = nextSize;
        DWORD actual = 0;
        memset(&m_over, 0, sizeof(m_over));
        m_over.hEvent = m_event.get();
        BOOL ret = isRead
                ? ReadFile(m_namedPipe.m_handle, m_buffer, nextSize, &actual, &m_over)
                : WriteFile(m_namedPipe.m_handle, m_buffer, nextSize, &actual, &m_over);
        if (!ret) {
            if (GetLastError() == ERROR_IO_PENDING) {
                // There is a pending I/O.
                m_pending = true;
                return progress;
            } else {
                // Pipe error.
                return ServiceResult::Error;
            }
        }
        ResetEvent(m_event.get());
        completeIo(actual);
        m_currentIoSize = 0;
        progress = ServiceResult::Progress;
    }
    return progress;
}

// This function is called after CancelIo has returned.  We need to block until
// the I/O operations have completed, which should happen very quickly.
// https://blogs.msdn.microsoft.com/oldnewthing/20110202-00/?p=11613
void NamedPipe::IoWorker::waitForCanceledIo()
{
    if (m_pending) {
        DWORD actual = 0;
        GetOverlappedResult(m_namedPipe.m_handle, &m_over, &actual, TRUE);
        m_pending = false;
    }
}

HANDLE NamedPipe::IoWorker::getWaitEvent()
{
    return m_pending ? m_event.get() : NULL;
}

void NamedPipe::InputWorker::completeIo(DWORD size)
{
    m_namedPipe.m_inQueue.append(m_buffer, size);
}

bool NamedPipe::InputWorker::shouldIssueIo(DWORD *size, bool *isRead)
{
    *isRead = true;
    ASSERT(!m_namedPipe.isConnecting());
    if (m_namedPipe.isClosed()) {
        return false;
    } else if (m_namedPipe.m_inQueue.size() < m_namedPipe.readBufferSize()) {
        *size = kIoSize;
        return true;
    } else {
        return false;
    }
}

void NamedPipe::OutputWorker::completeIo(DWORD size)
{
    ASSERT(size == m_currentIoSize);
}

bool NamedPipe::OutputWorker::shouldIssueIo(DWORD *size, bool *isRead)
{
    *isRead = false;
    if (!m_namedPipe.m_outQueue.empty()) {
        auto &out = m_namedPipe.m_outQueue;
        const DWORD writeSize = std::min<size_t>(out.size(), kIoSize);
        std::copy(&out[0], &out[writeSize], m_buffer);
        out.erase(0, writeSize);
        *size = writeSize;
        return true;
    } else {
        return false;
    }
}

DWORD NamedPipe::OutputWorker::getPendingIoSize()
{
    return m_pending ? m_currentIoSize : 0;
}

void NamedPipe::openServerPipe(LPCWSTR pipeName, OpenMode::t openMode,
                               int outBufferSize, int inBufferSize) {
    ASSERT(isClosed());
    ASSERT((openMode & OpenMode::Duplex) != 0);
    const DWORD winOpenMode =
              ((openMode & OpenMode::Reading) ? PIPE_ACCESS_INBOUND : 0)
            | ((openMode & OpenMode::Writing) ? PIPE_ACCESS_OUTBOUND : 0)
            | FILE_FLAG_FIRST_PIPE_INSTANCE
            | FILE_FLAG_OVERLAPPED;
    const auto sd = createPipeSecurityDescriptorOwnerFullControl();
    ASSERT(sd && "error creating data pipe SECURITY_DESCRIPTOR");
    SECURITY_ATTRIBUTES sa = {};
    sa.nLength = sizeof(sa);
    sa.lpSecurityDescriptor = sd.get();
    HANDLE handle = CreateNamedPipeW(
        pipeName,
        /*dwOpenMode=*/winOpenMode,
        /*dwPipeMode=*/rejectRemoteClientsPipeFlag(),
        /*nMaxInstances=*/1,
        /*nOutBufferSize=*/outBufferSize,
        /*nInBufferSize=*/inBufferSize,
        /*nDefaultTimeOut=*/30000,
        &sa);
    TRACE("opened server pipe [%s], handle == %p",
        utf8FromWide(pipeName).c_str(), handle);
    ASSERT(handle != INVALID_HANDLE_VALUE && "Could not open server pipe");
    m_name = pipeName;
    m_handle = handle;
    m_openMode = openMode;

    // Start an asynchronous connection attempt.
    m_connectEvent = createEvent();
    memset(&m_connectOver, 0, sizeof(m_connectOver));
    m_connectOver.hEvent = m_connectEvent.get();
    BOOL success = ConnectNamedPipe(m_handle, &m_connectOver);
    const auto err = GetLastError();
    if (!success && err == ERROR_PIPE_CONNECTED) {
        success = TRUE;
    }
    if (success) {
        TRACE("Server pipe [%s] connected", utf8FromWide(pipeName).c_str());
        m_connectEvent.dispose();
        startPipeWorkers();
    } else if (err != ERROR_IO_PENDING) {
        ASSERT(false && "ConnectNamedPipe call failed");
    }
}

void NamedPipe::connectToServer(LPCWSTR pipeName, OpenMode::t openMode)
{
    ASSERT(isClosed());
    ASSERT((openMode & OpenMode::Duplex) != 0);
    HANDLE handle = CreateFileW(
        pipeName,
        GENERIC_READ | GENERIC_WRITE,
        0,
        NULL,
        OPEN_EXISTING,
        SECURITY_SQOS_PRESENT | SECURITY_IDENTIFICATION | FILE_FLAG_OVERLAPPED,
        NULL);
    TRACE("connected to [%s], handle == %p",
        utf8FromWide(pipeName).c_str(), handle);
    ASSERT(handle != INVALID_HANDLE_VALUE && "Could not connect to pipe");
    m_name = pipeName;
    m_handle = handle;
    m_openMode = openMode;
    startPipeWorkers();
}

void NamedPipe::startPipeWorkers()
{
    if (m_openMode & OpenMode::Reading) {
        m_inputWorker.reset(new InputWorker(*this));
    }
    if (m_openMode & OpenMode::Writing) {
        m_outputWorker.reset(new OutputWorker(*this));
    }
}

size_t NamedPipe::bytesToSend()
{
    ASSERT(m_openMode & OpenMode::Writing);
    auto ret = m_outQueue.size();
    if (m_outputWorker != NULL) {
        ret += m_outputWorker->getPendingIoSize();
    }
    return ret;
}

void NamedPipe::write(const void *data, size_t size)
{
    ASSERT(m_openMode & OpenMode::Writing);
    m_outQueue.append(reinterpret_cast<const char*>(data), size);
}

void NamedPipe::write(const char *text)
{
    write(text, strlen(text));
}

size_t NamedPipe::readBufferSize()
{
    ASSERT(m_openMode & OpenMode::Reading);
    return m_readBufferSize;
}

void NamedPipe::setReadBufferSize(size_t size)
{
    ASSERT(m_openMode & OpenMode::Reading);
    m_readBufferSize = size;
}

size_t NamedPipe::bytesAvailable()
{
    ASSERT(m_openMode & OpenMode::Reading);
    return m_inQueue.size();
}

size_t NamedPipe::peek(void *data, size_t size)
{
    ASSERT(m_openMode & OpenMode::Reading);
    const auto out = reinterpret_cast<char*>(data);
    const size_t ret = std::min(size, m_inQueue.size());
    std::copy(&m_inQueue[0], &m_inQueue[size], out);
    return ret;
}

size_t NamedPipe::read(void *data, size_t size)
{
    size_t ret = peek(data, size);
    m_inQueue.erase(0, ret);
    return ret;
}

std::string NamedPipe::readToString(size_t size)
{
    ASSERT(m_openMode & OpenMode::Reading);
    size_t retSize = std::min(size, m_inQueue.size());
    std::string ret = m_inQueue.substr(0, retSize);
    m_inQueue.erase(0, retSize);
    return ret;
}

std::string NamedPipe::readAllToString()
{
    ASSERT(m_openMode & OpenMode::Reading);
    std::string ret = m_inQueue;
    m_inQueue.clear();
    return ret;
}

void NamedPipe::closePipe()
{
    if (m_handle == NULL) {
        return;
    }
    CancelIo(m_handle);
    if (m_connectEvent.get() != nullptr) {
        DWORD actual = 0;
        GetOverlappedResult(m_handle, &m_connectOver, &actual, TRUE);
        m_connectEvent.dispose();
    }
    if (m_inputWorker) {
        m_inputWorker->waitForCanceledIo();
        m_inputWorker.reset();
    }
    if (m_outputWorker) {
        m_outputWorker->waitForCanceledIo();
        m_outputWorker.reset();
    }
    CloseHandle(m_handle);
    m_handle = NULL;
}
<commit_msg>Fix a read-from-end-of-buffer issue<commit_after>// Copyright (c) 2011-2012 Ryan Prichard
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.

#include <string.h>

#include <algorithm>

#include "EventLoop.h"
#include "NamedPipe.h"
#include "../shared/DebugClient.h"
#include "../shared/StringUtil.h"
#include "../shared/WindowsSecurity.h"
#include "../shared/WinptyAssert.h"

// Returns true if anything happens (data received, data sent, pipe error).
bool NamedPipe::serviceIo(std::vector<HANDLE> *waitHandles)
{
    bool justConnected = false;
    const auto kError = ServiceResult::Error;
    const auto kProgress = ServiceResult::Progress;
    const auto kNoProgress = ServiceResult::NoProgress;
    if (m_handle == NULL) {
        return false;
    }
    if (m_connectEvent.get() != nullptr) {
        // We're still connecting this server pipe.  Check whether the pipe is
        // now connected.  If it isn't, add the pipe to the list of handles to
        // wait on.
        DWORD actual = 0;
        BOOL success =
            GetOverlappedResult(m_handle, &m_connectOver, &actual, FALSE);
        if (!success && GetLastError() == ERROR_PIPE_CONNECTED) {
            // I'm not sure this can happen, but it's easy to handle if it
            // does.
            success = TRUE;
        }
        if (!success) {
            ASSERT(GetLastError() == ERROR_IO_INCOMPLETE &&
                "Pended ConnectNamedPipe call failed");
            waitHandles->push_back(m_connectEvent.get());
        } else {
            TRACE("Server pipe [%s] connected",
                utf8FromWide(m_name).c_str());
            m_connectEvent.dispose();
            startPipeWorkers();
            justConnected = true;
        }
    }
    const auto readProgress = m_inputWorker ? m_inputWorker->service() : kNoProgress;
    const auto writeProgress = m_outputWorker ? m_outputWorker->service() : kNoProgress;
    if (readProgress == kError || writeProgress == kError) {
        closePipe();
        return true;
    }
    if (m_inputWorker && m_inputWorker->getWaitEvent() != nullptr) {
        waitHandles->push_back(m_inputWorker->getWaitEvent());
    }
    if (m_outputWorker && m_outputWorker->getWaitEvent() != nullptr) {
        waitHandles->push_back(m_outputWorker->getWaitEvent());
    }
    return justConnected
        || readProgress == kProgress
        || writeProgress == kProgress;
}

// manual reset, initially unset
static OwnedHandle createEvent() {
    HANDLE ret = CreateEventW(nullptr, TRUE, FALSE, nullptr);
    ASSERT(ret != nullptr && "CreateEventW failed");
    return OwnedHandle(ret);
}

NamedPipe::IoWorker::IoWorker(NamedPipe &namedPipe) :
    m_namedPipe(namedPipe),
    m_event(createEvent())
{
}

NamedPipe::ServiceResult NamedPipe::IoWorker::service()
{
    ServiceResult progress = ServiceResult::NoProgress;
    if (m_pending) {
        DWORD actual = 0;
        BOOL ret = GetOverlappedResult(m_namedPipe.m_handle, &m_over, &actual, FALSE);
        if (!ret) {
            if (GetLastError() == ERROR_IO_INCOMPLETE) {
                // There is a pending I/O.
                return progress;
            } else {
                // Pipe error.
                return ServiceResult::Error;
            }
        }
        ResetEvent(m_event.get());
        m_pending = false;
        completeIo(actual);
        m_currentIoSize = 0;
        progress = ServiceResult::Progress;
    }
    DWORD nextSize = 0;
    bool isRead = false;
    while (shouldIssueIo(&nextSize, &isRead)) {
        m_currentIoSize = nextSize;
        DWORD actual = 0;
        memset(&m_over, 0, sizeof(m_over));
        m_over.hEvent = m_event.get();
        BOOL ret = isRead
                ? ReadFile(m_namedPipe.m_handle, m_buffer, nextSize, &actual, &m_over)
                : WriteFile(m_namedPipe.m_handle, m_buffer, nextSize, &actual, &m_over);
        if (!ret) {
            if (GetLastError() == ERROR_IO_PENDING) {
                // There is a pending I/O.
                m_pending = true;
                return progress;
            } else {
                // Pipe error.
                return ServiceResult::Error;
            }
        }
        ResetEvent(m_event.get());
        completeIo(actual);
        m_currentIoSize = 0;
        progress = ServiceResult::Progress;
    }
    return progress;
}

// This function is called after CancelIo has returned.  We need to block until
// the I/O operations have completed, which should happen very quickly.
// https://blogs.msdn.microsoft.com/oldnewthing/20110202-00/?p=11613
void NamedPipe::IoWorker::waitForCanceledIo()
{
    if (m_pending) {
        DWORD actual = 0;
        GetOverlappedResult(m_namedPipe.m_handle, &m_over, &actual, TRUE);
        m_pending = false;
    }
}

HANDLE NamedPipe::IoWorker::getWaitEvent()
{
    return m_pending ? m_event.get() : NULL;
}

void NamedPipe::InputWorker::completeIo(DWORD size)
{
    m_namedPipe.m_inQueue.append(m_buffer, size);
}

bool NamedPipe::InputWorker::shouldIssueIo(DWORD *size, bool *isRead)
{
    *isRead = true;
    ASSERT(!m_namedPipe.isConnecting());
    if (m_namedPipe.isClosed()) {
        return false;
    } else if (m_namedPipe.m_inQueue.size() < m_namedPipe.readBufferSize()) {
        *size = kIoSize;
        return true;
    } else {
        return false;
    }
}

void NamedPipe::OutputWorker::completeIo(DWORD size)
{
    ASSERT(size == m_currentIoSize);
}

bool NamedPipe::OutputWorker::shouldIssueIo(DWORD *size, bool *isRead)
{
    *isRead = false;
    if (!m_namedPipe.m_outQueue.empty()) {
        auto &out = m_namedPipe.m_outQueue;
        const DWORD writeSize = std::min<size_t>(out.size(), kIoSize);
        std::copy(&out[0], &out[writeSize], m_buffer);
        out.erase(0, writeSize);
        *size = writeSize;
        return true;
    } else {
        return false;
    }
}

DWORD NamedPipe::OutputWorker::getPendingIoSize()
{
    return m_pending ? m_currentIoSize : 0;
}

void NamedPipe::openServerPipe(LPCWSTR pipeName, OpenMode::t openMode,
                               int outBufferSize, int inBufferSize) {
    ASSERT(isClosed());
    ASSERT((openMode & OpenMode::Duplex) != 0);
    const DWORD winOpenMode =
              ((openMode & OpenMode::Reading) ? PIPE_ACCESS_INBOUND : 0)
            | ((openMode & OpenMode::Writing) ? PIPE_ACCESS_OUTBOUND : 0)
            | FILE_FLAG_FIRST_PIPE_INSTANCE
            | FILE_FLAG_OVERLAPPED;
    const auto sd = createPipeSecurityDescriptorOwnerFullControl();
    ASSERT(sd && "error creating data pipe SECURITY_DESCRIPTOR");
    SECURITY_ATTRIBUTES sa = {};
    sa.nLength = sizeof(sa);
    sa.lpSecurityDescriptor = sd.get();
    HANDLE handle = CreateNamedPipeW(
        pipeName,
        /*dwOpenMode=*/winOpenMode,
        /*dwPipeMode=*/rejectRemoteClientsPipeFlag(),
        /*nMaxInstances=*/1,
        /*nOutBufferSize=*/outBufferSize,
        /*nInBufferSize=*/inBufferSize,
        /*nDefaultTimeOut=*/30000,
        &sa);
    TRACE("opened server pipe [%s], handle == %p",
        utf8FromWide(pipeName).c_str(), handle);
    ASSERT(handle != INVALID_HANDLE_VALUE && "Could not open server pipe");
    m_name = pipeName;
    m_handle = handle;
    m_openMode = openMode;

    // Start an asynchronous connection attempt.
    m_connectEvent = createEvent();
    memset(&m_connectOver, 0, sizeof(m_connectOver));
    m_connectOver.hEvent = m_connectEvent.get();
    BOOL success = ConnectNamedPipe(m_handle, &m_connectOver);
    const auto err = GetLastError();
    if (!success && err == ERROR_PIPE_CONNECTED) {
        success = TRUE;
    }
    if (success) {
        TRACE("Server pipe [%s] connected", utf8FromWide(pipeName).c_str());
        m_connectEvent.dispose();
        startPipeWorkers();
    } else if (err != ERROR_IO_PENDING) {
        ASSERT(false && "ConnectNamedPipe call failed");
    }
}

void NamedPipe::connectToServer(LPCWSTR pipeName, OpenMode::t openMode)
{
    ASSERT(isClosed());
    ASSERT((openMode & OpenMode::Duplex) != 0);
    HANDLE handle = CreateFileW(
        pipeName,
        GENERIC_READ | GENERIC_WRITE,
        0,
        NULL,
        OPEN_EXISTING,
        SECURITY_SQOS_PRESENT | SECURITY_IDENTIFICATION | FILE_FLAG_OVERLAPPED,
        NULL);
    TRACE("connected to [%s], handle == %p",
        utf8FromWide(pipeName).c_str(), handle);
    ASSERT(handle != INVALID_HANDLE_VALUE && "Could not connect to pipe");
    m_name = pipeName;
    m_handle = handle;
    m_openMode = openMode;
    startPipeWorkers();
}

void NamedPipe::startPipeWorkers()
{
    if (m_openMode & OpenMode::Reading) {
        m_inputWorker.reset(new InputWorker(*this));
    }
    if (m_openMode & OpenMode::Writing) {
        m_outputWorker.reset(new OutputWorker(*this));
    }
}

size_t NamedPipe::bytesToSend()
{
    ASSERT(m_openMode & OpenMode::Writing);
    auto ret = m_outQueue.size();
    if (m_outputWorker != NULL) {
        ret += m_outputWorker->getPendingIoSize();
    }
    return ret;
}

void NamedPipe::write(const void *data, size_t size)
{
    ASSERT(m_openMode & OpenMode::Writing);
    m_outQueue.append(reinterpret_cast<const char*>(data), size);
}

void NamedPipe::write(const char *text)
{
    write(text, strlen(text));
}

size_t NamedPipe::readBufferSize()
{
    ASSERT(m_openMode & OpenMode::Reading);
    return m_readBufferSize;
}

void NamedPipe::setReadBufferSize(size_t size)
{
    ASSERT(m_openMode & OpenMode::Reading);
    m_readBufferSize = size;
}

size_t NamedPipe::bytesAvailable()
{
    ASSERT(m_openMode & OpenMode::Reading);
    return m_inQueue.size();
}

size_t NamedPipe::peek(void *data, size_t size)
{
    ASSERT(m_openMode & OpenMode::Reading);
    const auto out = reinterpret_cast<char*>(data);
    const size_t ret = std::min(size, m_inQueue.size());
    std::copy(&m_inQueue[0], &m_inQueue[ret], out);
    return ret;
}

size_t NamedPipe::read(void *data, size_t size)
{
    size_t ret = peek(data, size);
    m_inQueue.erase(0, ret);
    return ret;
}

std::string NamedPipe::readToString(size_t size)
{
    ASSERT(m_openMode & OpenMode::Reading);
    size_t retSize = std::min(size, m_inQueue.size());
    std::string ret = m_inQueue.substr(0, retSize);
    m_inQueue.erase(0, retSize);
    return ret;
}

std::string NamedPipe::readAllToString()
{
    ASSERT(m_openMode & OpenMode::Reading);
    std::string ret = m_inQueue;
    m_inQueue.clear();
    return ret;
}

void NamedPipe::closePipe()
{
    if (m_handle == NULL) {
        return;
    }
    CancelIo(m_handle);
    if (m_connectEvent.get() != nullptr) {
        DWORD actual = 0;
        GetOverlappedResult(m_handle, &m_connectOver, &actual, TRUE);
        m_connectEvent.dispose();
    }
    if (m_inputWorker) {
        m_inputWorker->waitForCanceledIo();
        m_inputWorker.reset();
    }
    if (m_outputWorker) {
        m_outputWorker->waitForCanceledIo();
        m_outputWorker.reset();
    }
    CloseHandle(m_handle);
    m_handle = NULL;
}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org 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 version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_desktop.hxx"

#include "dp_update.hxx"
#include "dp_version.hxx"
#include "dp_identifier.hxx"
#include "dp_descriptioninfoset.hxx"

#include "rtl/bootstrap.hxx"

using namespace ::com::sun::star;
using namespace ::com::sun::star::uno;
using ::rtl::OUString;
using ::rtl::OString;

namespace dp_misc {
namespace {

int determineHighestVersion(
    ::rtl::OUString const & userVersion,
    ::rtl::OUString const & sharedVersion,
    ::rtl::OUString const & bundledVersion,
    ::rtl::OUString const & onlineVersion)
{
    int index = 0;
    OUString  greatest = userVersion;
    if (dp_misc::compareVersions(sharedVersion, greatest) == dp_misc::GREATER)
    {
        index = 1;
        greatest = sharedVersion;
    }
    if (dp_misc::compareVersions(bundledVersion, greatest) == dp_misc::GREATER)
    {
        index = 2;
        greatest = bundledVersion;
    }
    if (dp_misc::compareVersions(onlineVersion, greatest) == dp_misc::GREATER)
    {
        index = 3;
    }
    return index;
}

Sequence< Reference< xml::dom::XElement > >
getUpdateInformation( Reference<deployment::XUpdateInformationProvider > const & updateInformation,
                      Sequence< OUString > const & urls,
                      OUString const & identifier,
                      uno::Any & out_error)
{
    try {
        return updateInformation->getUpdateInformation(urls, identifier);
    } catch (uno::RuntimeException &) {
        throw;
    } catch (ucb::CommandFailedException & e) {
        out_error = e.Reason;
    } catch (ucb::CommandAbortedException &) {
    } catch (uno::Exception & e) {
        out_error = uno::makeAny(e);
    }
    return
        Sequence<Reference< xml::dom::XElement > >();
}

//Put in anonymous namespace

void getOwnUpdateInfos(
        Reference<uno::XComponentContext> const & xContext,
        Reference<deployment::XUpdateInformationProvider > const &  updateInformation,
        UpdateInfoMap& inout_map, std::vector<std::pair<Reference<deployment::XPackage>, uno::Any> > & out_errors,
        bool & out_allFound)
{
    bool allHaveOwnUpdateInformation = true;
    for (UpdateInfoMap::iterator i = inout_map.begin(); i != inout_map.end(); i++)
    {
        OSL_ASSERT(i->second.extension.is());
        Sequence<OUString> urls(i->second.extension->getUpdateInformationURLs());
        if (urls.getLength())
        {
            const OUString id =  dp_misc::getIdentifier(i->second.extension);
            uno::Any anyError;
            //It is unclear from the idl if there can be a null reference returned.
            //However all valid information should be the same
            Sequence<Reference< xml::dom::XElement > >
                infos(getUpdateInformation(updateInformation, urls, id, anyError));
            if (anyError.hasValue())
                out_errors.push_back(std::make_pair(i->second.extension, anyError));

            for (sal_Int32 j = 0; j < infos.getLength(); ++j)
            {
                dp_misc::DescriptionInfoset infoset(
                    xContext,
                    Reference< xml::dom::XNode >(infos[j], UNO_QUERY_THROW));
                if (!infoset.hasDescription())
                    continue;
                boost::optional< OUString > id2(infoset.getIdentifier());
                if (!id2)
                    continue;
                OSL_ASSERT(*id2 == id);
                if (*id2 == id)
                {
                    i->second.version = infoset.getVersion();
                    i->second.info = Reference< xml::dom::XNode >(
                        infos[j], UNO_QUERY_THROW);
                }
                break;
            }
        }
        else
        {
            allHaveOwnUpdateInformation &= false;
        }
    }
    out_allFound = allHaveOwnUpdateInformation;
}

void getDefaultUpdateInfos(
    Reference<uno::XComponentContext> const & xContext,
    Reference<deployment::XUpdateInformationProvider > const &  updateInformation,
    UpdateInfoMap& inout_map,
     std::vector<std::pair<Reference<deployment::XPackage>, uno::Any> > & out_errors)
{
    const rtl::OUString sDefaultURL(dp_misc::getExtensionDefaultUpdateURL());
    OSL_ASSERT(sDefaultURL.getLength());

    Any anyError;
    Sequence< Reference< xml::dom::XElement > >
        infos(
            getUpdateInformation(
                updateInformation,
                Sequence< OUString >(&sDefaultURL, 1), OUString(), anyError));
    if (anyError.hasValue())
        out_errors.push_back(std::make_pair(Reference<deployment::XPackage>(), anyError));
    for (sal_Int32 i = 0; i < infos.getLength(); ++i)
    {
        Reference< xml::dom::XNode > node(infos[i], UNO_QUERY_THROW);
        dp_misc::DescriptionInfoset infoset(xContext, node);
        boost::optional< OUString > id(infoset.getIdentifier());
        if (!id) {
            continue;
        }
        UpdateInfoMap::iterator j = inout_map.find(*id);
        if (j != inout_map.end())
        {
            //skip those extension which provide its own update urls
            if (j->second.extension->getUpdateInformationURLs().getLength())
                continue;
            OUString v(infoset.getVersion());
            //look for the highest version in the online repository
            if (dp_misc::compareVersions(v, j->second.version) ==
                dp_misc::GREATER)
            {
                j->second.version = v;
                j->second.info = node;
            }
        }
    }
}

} // anon namespace

OUString getExtensionDefaultUpdateURL()
{
    ::rtl::OUString sUrl(
        RTL_CONSTASCII_USTRINGPARAM(
        "${$BRAND_BASE_DIR/program/" SAL_CONFIGFILE("version")
        ":Version:ExtensionUpdateURL}"));
    ::rtl::Bootstrap::expandMacros(sUrl);
    return sUrl;
}

/* returns the index of the greatest version, starting with 0

 */
UPDATE_SOURCE isUpdateUserExtension(
    bool bReadOnlyShared,
    ::rtl::OUString const & userVersion,
    ::rtl::OUString const & sharedVersion,
    ::rtl::OUString const & bundledVersion,
    ::rtl::OUString const & onlineVersion)
{
    UPDATE_SOURCE retVal = UPDATE_SOURCE_NONE;
    if (bReadOnlyShared)
    {
        if (userVersion.getLength())
        {
            int index = determineHighestVersion(
                userVersion, sharedVersion, bundledVersion, onlineVersion);
            if (index == 1)
                retVal = UPDATE_SOURCE_SHARED;
            else if (index == 2)
                retVal = UPDATE_SOURCE_BUNDLED;
            else if (index == 3)
                retVal = UPDATE_SOURCE_ONLINE;
        }
        else if (sharedVersion.getLength())
        {
            int index = determineHighestVersion(
                OUString(), sharedVersion, bundledVersion, onlineVersion);
            if (index == 2)
                retVal = UPDATE_SOURCE_BUNDLED;
            else if (index == 3)
                retVal = UPDATE_SOURCE_ONLINE;

        }
        else if (bundledVersion.getLength())
        {
            int index = determineHighestVersion(
                OUString(), OUString(), bundledVersion, onlineVersion);
            if (index == 3)
                retVal = UPDATE_SOURCE_ONLINE;
        }
    }
    else
    {
        if (userVersion.getLength())
        {
            int index = determineHighestVersion(
                userVersion, sharedVersion, bundledVersion, onlineVersion);
            if (index == 1)
                retVal = UPDATE_SOURCE_SHARED;
            else if (index == 2)
                retVal = UPDATE_SOURCE_BUNDLED;
            else if (index == 3)
                retVal = UPDATE_SOURCE_ONLINE;
        }
    }

    return retVal;
}

UPDATE_SOURCE isUpdateSharedExtension(
    bool bReadOnlyShared,
    ::rtl::OUString const & sharedVersion,
    ::rtl::OUString const & bundledVersion,
    ::rtl::OUString const & onlineVersion)
{
    if (bReadOnlyShared)
        return UPDATE_SOURCE_NONE;
    UPDATE_SOURCE retVal = UPDATE_SOURCE_NONE;

    if (sharedVersion.getLength())
    {
        int index = determineHighestVersion(
            OUString(), sharedVersion, bundledVersion, onlineVersion);
        if (index == 2)
            retVal = UPDATE_SOURCE_BUNDLED;
        else if (index == 3)
            retVal = UPDATE_SOURCE_ONLINE;
    }
    else if (bundledVersion.getLength())
    {
        int index = determineHighestVersion(
            OUString(), OUString(), bundledVersion, onlineVersion);
        if (index == 3)
            retVal = UPDATE_SOURCE_ONLINE;
    }
    return retVal;
}

Reference<deployment::XPackage>
getExtensionWithHighestVersion(
    Sequence<Reference<deployment::XPackage> > const & seqExt)
{
    if (seqExt.getLength() == 0)
        return Reference<deployment::XPackage>();

    Reference<deployment::XPackage> greatest;
    sal_Int32 len = seqExt.getLength();

    for (sal_Int32 i = 0; i < len; i++)
    {
        if (!greatest.is())
        {
            greatest = seqExt[i];
            continue;
        }
        Reference<deployment::XPackage> const & current = seqExt[i];
        //greatest has a value
        if (! current.is())
            continue;

        if (dp_misc::compareVersions(current->getVersion(), greatest->getVersion()) == dp_misc::GREATER)
            greatest = current;
    }
    return greatest;
}

UpdateInfo::UpdateInfo( Reference< deployment::XPackage> const & ext):
extension(ext)
{
}

UpdateInfoMap getOnlineUpdateInfos(
    Reference<uno::XComponentContext> const &xContext,
    Reference<deployment::XExtensionManager> const & xExtMgr,
    Reference<deployment::XUpdateInformationProvider > const & updateInformation,
    std::vector<Reference<deployment::XPackage > > const * extensionList,
    std::vector<std::pair< Reference<deployment::XPackage>, uno::Any> > & out_errors)
{
    OSL_ASSERT(xExtMgr.is());
    UpdateInfoMap infoMap;
    if (!xExtMgr.is())
        return infoMap;

    if (!extensionList)
    {
        const uno::Sequence< uno::Sequence< Reference<deployment::XPackage > > > seqAllExt =  xExtMgr->getAllExtensions(
            Reference<task::XAbortChannel>(), Reference<ucb::XCommandEnvironment>());

        //fill the UpdateInfoMap. key = extension identifier, value = UpdateInfo
        for (int pos = seqAllExt.getLength(); pos --; )
        {
            uno::Sequence<Reference<deployment::XPackage> > const &   seqExt = seqAllExt[pos];

            Reference<deployment::XPackage> extension = getExtensionWithHighestVersion(seqExt);
            OSL_ASSERT(extension.is());

            std::pair<UpdateInfoMap::iterator, bool> insertRet = infoMap.insert(
                UpdateInfoMap::value_type(
                    dp_misc::getIdentifier(extension), UpdateInfo(extension)));
            OSL_ASSERT(insertRet.second == true);
        }
    }
    else
    {
        typedef std::vector<Reference<deployment::XPackage > >::const_iterator CIT;
        for (CIT i = extensionList->begin(); i != extensionList->end(); i++)
        {
            OSL_ASSERT(i->is());
            std::pair<UpdateInfoMap::iterator, bool> insertRet = infoMap.insert(
                UpdateInfoMap::value_type(
                    dp_misc::getIdentifier(*i), UpdateInfo(*i)));
            OSL_ASSERT(insertRet.second == true);
        }
    }

    //Now find the update information for the extensions which provide their own
    //URLs to update information.
    bool allInfosObtained = false;
    getOwnUpdateInfos(xContext, updateInformation, infoMap, out_errors, allInfosObtained);

    if (!allInfosObtained)
        getDefaultUpdateInfos(xContext, updateInformation, infoMap, out_errors);
    return infoMap;
}
OUString getHighestVersion(
    ::rtl::OUString const & userVersion,
    ::rtl::OUString const & sharedVersion,
    ::rtl::OUString const & bundledVersion,
    ::rtl::OUString const & onlineVersion)
{
    int index = determineHighestVersion(userVersion, sharedVersion, bundledVersion, onlineVersion);
    switch (index)
    {
    case 0: return userVersion;
    case 1: return sharedVersion;
    case 2: return bundledVersion;
    case 3: return onlineVersion;
    default: OSL_ASSERT(0);
    }

    return OUString();
}
} //namespace dp_misc
<commit_msg>dv21 #i113524# prevent online update for bundled extensions<commit_after>/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org 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 version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_desktop.hxx"

#include "dp_update.hxx"
#include "dp_version.hxx"
#include "dp_identifier.hxx"
#include "dp_descriptioninfoset.hxx"

#include "rtl/bootstrap.hxx"

using namespace ::com::sun::star;
using namespace ::com::sun::star::uno;
using ::rtl::OUString;
using ::rtl::OString;

namespace dp_misc {
namespace {

int determineHighestVersion(
    ::rtl::OUString const & userVersion,
    ::rtl::OUString const & sharedVersion,
    ::rtl::OUString const & bundledVersion,
    ::rtl::OUString const & onlineVersion)
{
    int index = 0;
    OUString  greatest = userVersion;
    if (dp_misc::compareVersions(sharedVersion, greatest) == dp_misc::GREATER)
    {
        index = 1;
        greatest = sharedVersion;
    }
    if (dp_misc::compareVersions(bundledVersion, greatest) == dp_misc::GREATER)
    {
        index = 2;
        greatest = bundledVersion;
    }
    if (dp_misc::compareVersions(onlineVersion, greatest) == dp_misc::GREATER)
    {
        index = 3;
    }
    return index;
}

Sequence< Reference< xml::dom::XElement > >
getUpdateInformation( Reference<deployment::XUpdateInformationProvider > const & updateInformation,
                      Sequence< OUString > const & urls,
                      OUString const & identifier,
                      uno::Any & out_error)
{
    try {
        return updateInformation->getUpdateInformation(urls, identifier);
    } catch (uno::RuntimeException &) {
        throw;
    } catch (ucb::CommandFailedException & e) {
        out_error = e.Reason;
    } catch (ucb::CommandAbortedException &) {
    } catch (uno::Exception & e) {
        out_error = uno::makeAny(e);
    }
    return
        Sequence<Reference< xml::dom::XElement > >();
}

void getOwnUpdateInfos(
        Reference<uno::XComponentContext> const & xContext,
        Reference<deployment::XUpdateInformationProvider > const &  updateInformation,
        UpdateInfoMap& inout_map, std::vector<std::pair<Reference<deployment::XPackage>, uno::Any> > & out_errors,
        bool & out_allFound)
{
    bool allHaveOwnUpdateInformation = true;
    for (UpdateInfoMap::iterator i = inout_map.begin(); i != inout_map.end(); i++)
    {
        OSL_ASSERT(i->second.extension.is());
        Sequence<OUString> urls(i->second.extension->getUpdateInformationURLs());
        if (urls.getLength())
        {
            const OUString id =  dp_misc::getIdentifier(i->second.extension);
            uno::Any anyError;
            //It is unclear from the idl if there can be a null reference returned.
            //However all valid information should be the same
            Sequence<Reference< xml::dom::XElement > >
                infos(getUpdateInformation(updateInformation, urls, id, anyError));
            if (anyError.hasValue())
                out_errors.push_back(std::make_pair(i->second.extension, anyError));

            for (sal_Int32 j = 0; j < infos.getLength(); ++j)
            {
                dp_misc::DescriptionInfoset infoset(
                    xContext,
                    Reference< xml::dom::XNode >(infos[j], UNO_QUERY_THROW));
                if (!infoset.hasDescription())
                    continue;
                boost::optional< OUString > id2(infoset.getIdentifier());
                if (!id2)
                    continue;
                OSL_ASSERT(*id2 == id);
                if (*id2 == id)
                {
                    i->second.version = infoset.getVersion();
                    i->second.info = Reference< xml::dom::XNode >(
                        infos[j], UNO_QUERY_THROW);
                }
                break;
            }
        }
        else
        {
            allHaveOwnUpdateInformation &= false;
        }
    }
    out_allFound = allHaveOwnUpdateInformation;
}

void getDefaultUpdateInfos(
    Reference<uno::XComponentContext> const & xContext,
    Reference<deployment::XUpdateInformationProvider > const &  updateInformation,
    UpdateInfoMap& inout_map,
     std::vector<std::pair<Reference<deployment::XPackage>, uno::Any> > & out_errors)
{
    const rtl::OUString sDefaultURL(dp_misc::getExtensionDefaultUpdateURL());
    OSL_ASSERT(sDefaultURL.getLength());

    Any anyError;
    Sequence< Reference< xml::dom::XElement > >
        infos(
            getUpdateInformation(
                updateInformation,
                Sequence< OUString >(&sDefaultURL, 1), OUString(), anyError));
    if (anyError.hasValue())
        out_errors.push_back(std::make_pair(Reference<deployment::XPackage>(), anyError));
    for (sal_Int32 i = 0; i < infos.getLength(); ++i)
    {
        Reference< xml::dom::XNode > node(infos[i], UNO_QUERY_THROW);
        dp_misc::DescriptionInfoset infoset(xContext, node);
        boost::optional< OUString > id(infoset.getIdentifier());
        if (!id) {
            continue;
        }
        UpdateInfoMap::iterator j = inout_map.find(*id);
        if (j != inout_map.end())
        {
            //skip those extension which provide its own update urls
            if (j->second.extension->getUpdateInformationURLs().getLength())
                continue;
            OUString v(infoset.getVersion());
            //look for the highest version in the online repository
            if (dp_misc::compareVersions(v, j->second.version) ==
                dp_misc::GREATER)
            {
                j->second.version = v;
                j->second.info = node;
            }
        }
    }
}

bool containsBundledOnly(Sequence<Reference<deployment::XPackage> > const & sameIdExtensions)
{
    OSL_ASSERT(sameIdExtensions.getLength() == 3);
    if (!sameIdExtensions[0].is() && !sameIdExtensions[1].is() && sameIdExtensions[2].is())
        return true;
    else
        return false;
}
/** Returns true if the list of extensions are bundled extensions and there are no
    other extensions with the same identifier in the shared or user repository.
    If extensionList is NULL, then it is checked if there are only bundled extensions.
*/
bool onlyBundledExtensions(
    Reference<deployment::XExtensionManager> const & xExtMgr,
    std::vector< Reference<deployment::XPackage > > const * extensionList)
{
    OSL_ASSERT(xExtMgr.is());
    bool onlyBundled = true;
    if (extensionList)
    {
        typedef std::vector<Reference<deployment::XPackage > >::const_iterator CIT;
        for (CIT i = extensionList->begin(); i != extensionList->end(); i++)
        {
            Sequence<Reference<deployment::XPackage> > seqExt = xExtMgr->getExtensionsWithSameIdentifier(
                dp_misc::getIdentifier(*i), (*i)->getName(), Reference<ucb::XCommandEnvironment>());

            if (!containsBundledOnly(seqExt))
            {
                onlyBundled = false;
                break;
            }

        }
    }
    else
    {
        const uno::Sequence< uno::Sequence< Reference<deployment::XPackage > > > seqAllExt =
            xExtMgr->getAllExtensions(Reference<task::XAbortChannel>(), Reference<ucb::XCommandEnvironment>());

        for (int pos = seqAllExt.getLength(); pos --; )
        {
            if (!containsBundledOnly(seqAllExt[pos]))
            {
                onlyBundled = false;
                break;
            }
        }
    }
    return onlyBundled;
}

} // anon namespace

OUString getExtensionDefaultUpdateURL()
{
    ::rtl::OUString sUrl(
        RTL_CONSTASCII_USTRINGPARAM(
        "${$BRAND_BASE_DIR/program/" SAL_CONFIGFILE("version")
        ":Version:ExtensionUpdateURL}"));
    ::rtl::Bootstrap::expandMacros(sUrl);
    return sUrl;
}

/* returns the index of the greatest version, starting with 0

 */
UPDATE_SOURCE isUpdateUserExtension(
    bool bReadOnlyShared,
    ::rtl::OUString const & userVersion,
    ::rtl::OUString const & sharedVersion,
    ::rtl::OUString const & bundledVersion,
    ::rtl::OUString const & onlineVersion)
{
    UPDATE_SOURCE retVal = UPDATE_SOURCE_NONE;
    if (bReadOnlyShared)
    {
        if (userVersion.getLength())
        {
            int index = determineHighestVersion(
                userVersion, sharedVersion, bundledVersion, onlineVersion);
            if (index == 1)
                retVal = UPDATE_SOURCE_SHARED;
            else if (index == 2)
                retVal = UPDATE_SOURCE_BUNDLED;
            else if (index == 3)
                retVal = UPDATE_SOURCE_ONLINE;
        }
        else if (sharedVersion.getLength())
        {
            int index = determineHighestVersion(
                OUString(), sharedVersion, bundledVersion, onlineVersion);
            if (index == 2)
                retVal = UPDATE_SOURCE_BUNDLED;
            else if (index == 3)
                retVal = UPDATE_SOURCE_ONLINE;

        }
        //No update for bundled extensions, they are updated only by the setup
        //else if (bundledVersion.getLength())
        //{
        //    int index = determineHighestVersion(
        //        OUString(), OUString(), bundledVersion, onlineVersion);
        //    if (index == 3)
        //        retVal = UPDATE_SOURCE_ONLINE;
        //}
    }
    else
    {
        if (userVersion.getLength())
        {
            int index = determineHighestVersion(
                userVersion, sharedVersion, bundledVersion, onlineVersion);
            if (index == 1)
                retVal = UPDATE_SOURCE_SHARED;
            else if (index == 2)
                retVal = UPDATE_SOURCE_BUNDLED;
            else if (index == 3)
                retVal = UPDATE_SOURCE_ONLINE;
        }
    }

    return retVal;
}

UPDATE_SOURCE isUpdateSharedExtension(
    bool bReadOnlyShared,
    ::rtl::OUString const & sharedVersion,
    ::rtl::OUString const & bundledVersion,
    ::rtl::OUString const & onlineVersion)
{
    if (bReadOnlyShared)
        return UPDATE_SOURCE_NONE;
    UPDATE_SOURCE retVal = UPDATE_SOURCE_NONE;

    if (sharedVersion.getLength())
    {
        int index = determineHighestVersion(
            OUString(), sharedVersion, bundledVersion, onlineVersion);
        if (index == 2)
            retVal = UPDATE_SOURCE_BUNDLED;
        else if (index == 3)
            retVal = UPDATE_SOURCE_ONLINE;
    }
    //No update for bundled extensions, they are updated only by the setup
    //else if (bundledVersion.getLength())
    //{
    //    int index = determineHighestVersion(
    //        OUString(), OUString(), bundledVersion, onlineVersion);
    //    if (index == 3)
    //        retVal = UPDATE_SOURCE_ONLINE;
    //}
    return retVal;
}

Reference<deployment::XPackage>
getExtensionWithHighestVersion(
    Sequence<Reference<deployment::XPackage> > const & seqExt)
{
    if (seqExt.getLength() == 0)
        return Reference<deployment::XPackage>();

    Reference<deployment::XPackage> greatest;
    sal_Int32 len = seqExt.getLength();

    for (sal_Int32 i = 0; i < len; i++)
    {
        if (!greatest.is())
        {
            greatest = seqExt[i];
            continue;
        }
        Reference<deployment::XPackage> const & current = seqExt[i];
        //greatest has a value
        if (! current.is())
            continue;

        if (dp_misc::compareVersions(current->getVersion(), greatest->getVersion()) == dp_misc::GREATER)
            greatest = current;
    }
    return greatest;
}

UpdateInfo::UpdateInfo( Reference< deployment::XPackage> const & ext):
extension(ext)
{
}

UpdateInfoMap getOnlineUpdateInfos(
    Reference<uno::XComponentContext> const &xContext,
    Reference<deployment::XExtensionManager> const & xExtMgr,
    Reference<deployment::XUpdateInformationProvider > const & updateInformation,
    std::vector<Reference<deployment::XPackage > > const * extensionList,
    std::vector<std::pair< Reference<deployment::XPackage>, uno::Any> > & out_errors)
{
    OSL_ASSERT(xExtMgr.is());
    UpdateInfoMap infoMap;
    if (!xExtMgr.is() || onlyBundledExtensions(xExtMgr, extensionList))
        return infoMap;

    if (!extensionList)
    {
        const uno::Sequence< uno::Sequence< Reference<deployment::XPackage > > > seqAllExt =  xExtMgr->getAllExtensions(
            Reference<task::XAbortChannel>(), Reference<ucb::XCommandEnvironment>());

        //fill the UpdateInfoMap. key = extension identifier, value = UpdateInfo
        for (int pos = seqAllExt.getLength(); pos --; )
        {
            uno::Sequence<Reference<deployment::XPackage> > const &   seqExt = seqAllExt[pos];

            Reference<deployment::XPackage> extension = getExtensionWithHighestVersion(seqExt);
            OSL_ASSERT(extension.is());

            std::pair<UpdateInfoMap::iterator, bool> insertRet = infoMap.insert(
                UpdateInfoMap::value_type(
                    dp_misc::getIdentifier(extension), UpdateInfo(extension)));
            OSL_ASSERT(insertRet.second == true);
        }
    }
    else
    {
        typedef std::vector<Reference<deployment::XPackage > >::const_iterator CIT;
        for (CIT i = extensionList->begin(); i != extensionList->end(); i++)
        {
            OSL_ASSERT(i->is());
            std::pair<UpdateInfoMap::iterator, bool> insertRet = infoMap.insert(
                UpdateInfoMap::value_type(
                    dp_misc::getIdentifier(*i), UpdateInfo(*i)));
            OSL_ASSERT(insertRet.second == true);
        }
    }

    //Now find the update information for the extensions which provide their own
    //URLs to update information.
    bool allInfosObtained = false;
    getOwnUpdateInfos(xContext, updateInformation, infoMap, out_errors, allInfosObtained);

    if (!allInfosObtained)
        getDefaultUpdateInfos(xContext, updateInformation, infoMap, out_errors);
    return infoMap;
}
OUString getHighestVersion(
    ::rtl::OUString const & userVersion,
    ::rtl::OUString const & sharedVersion,
    ::rtl::OUString const & bundledVersion,
    ::rtl::OUString const & onlineVersion)
{
    int index = determineHighestVersion(userVersion, sharedVersion, bundledVersion, onlineVersion);
    switch (index)
    {
    case 0: return userVersion;
    case 1: return sharedVersion;
    case 2: return bundledVersion;
    case 3: return onlineVersion;
    default: OSL_ASSERT(0);
    }

    return OUString();
}
} //namespace dp_misc
<|endoftext|>


<commit_before>#include "resources/TextureData.h"

#include "math/Misc.h"
#include "renderers/Renderer.h"
#include "resources/ResourceManager.h"
#include "ImageIO.h"
#include "Log.h"
#include <nanosvg/nanosvg.h>
#include <nanosvg/nanosvgrast.h>
#include <assert.h>
#include <string.h>

#define DPI 96

TextureData::TextureData(bool tile) : mTile(tile), mTextureID(0), mDataRGBA(nullptr), mScalable(false),
									  mWidth(0), mHeight(0), mSourceWidth(0.0f), mSourceHeight(0.0f)
{
}

TextureData::~TextureData()
{
	releaseVRAM();
	releaseRAM();
}

void TextureData::initFromPath(const std::string& path)
{
	// Just set the path. It will be loaded later
	mPath = path;
	// Only textures with paths are reloadable
	mReloadable = true;
}

bool TextureData::initSVGFromMemory(const unsigned char* fileData, size_t length)
{
	// If already initialised then don't read again
	std::unique_lock<std::mutex> lock(mMutex);
	if (mDataRGBA)
		return true;

	// nsvgParse excepts a modifiable, null-terminated string
	char* copy = (char*)malloc(length + 1);
	assert(copy != NULL);
	memcpy(copy, fileData, length);
	copy[length] = '\0';

	NSVGimage* svgImage = nsvgParse(copy, "px", DPI);
	free(copy);
	if (!svgImage)
	{
		LOG(LogError) << "Error parsing SVG image.";
		return false;
	}

	// We want to rasterise this texture at a specific resolution. If the source size
	// variables are set then use them otherwise set them from the parsed file
	if ((mSourceWidth == 0.0f) && (mSourceHeight == 0.0f))
	{
		mSourceWidth = svgImage->width;
		mSourceHeight = svgImage->height;
	}
	mWidth = (size_t)Math::round(mSourceWidth);
	mHeight = (size_t)Math::round(mSourceHeight);

	if (mWidth == 0)
	{
		// auto scale width to keep aspect
		mWidth = (size_t)Math::round(((float)mHeight / svgImage->height) * svgImage->width);
	}
	else if (mHeight == 0)
	{
		// auto scale height to keep aspect
		mHeight = (size_t)Math::round(((float)mWidth / svgImage->width) * svgImage->height);
	}

	unsigned char* dataRGBA = new unsigned char[mWidth * mHeight * 4];

	NSVGrasterizer* rast = nsvgCreateRasterizer();
	float scale = Math::min(mHeight / svgImage->height, mWidth / svgImage->width);
	nsvgRasterize(rast, svgImage, 0, 0, scale, dataRGBA, (int)mWidth, (int)mHeight, (int)mWidth * 4);
	nsvgDeleteRasterizer(rast);

	ImageIO::flipPixelsVert(dataRGBA, mWidth, mHeight);

	mDataRGBA = dataRGBA;

	return true;
}

bool TextureData::initImageFromMemory(const unsigned char* fileData, size_t length)
{
	size_t width, height;

	// If already initialised then don't read again
	{
		std::unique_lock<std::mutex> lock(mMutex);
		if (mDataRGBA)
			return true;
	}

	std::vector<unsigned char> imageRGBA = ImageIO::loadFromMemoryRGBA32((const unsigned char*)(fileData), length, width, height);
	if (imageRGBA.size() == 0)
	{
		LOG(LogError) << "Could not initialize texture from memory, invalid data!  (file path: " << mPath << ", data ptr: " << (size_t)fileData << ", reported size: " << length << ")";
		return false;
	}

	mSourceWidth = (float) width;
	mSourceHeight = (float) height;
	mScalable = false;

	return initFromRGBA(imageRGBA.data(), width, height);
}

bool TextureData::initFromRGBA(const unsigned char* dataRGBA, size_t width, size_t height)
{
	// If already initialised then don't read again
	std::unique_lock<std::mutex> lock(mMutex);
	if (mDataRGBA)
		return true;

	// Take a copy
	mDataRGBA = new unsigned char[width * height * 4];
	memcpy(mDataRGBA, dataRGBA, width * height * 4);
	mWidth = width;
	mHeight = height;
	return true;
}

bool TextureData::load()
{
	bool retval = false;

	// Need to load. See if there is a file
	if (!mPath.empty())
	{
		std::shared_ptr<ResourceManager>& rm = ResourceManager::getInstance();
		const ResourceData& data = rm->getFileData(mPath);
		// is it an SVG?
		if (mPath.substr(mPath.size() - 4, std::string::npos) == ".svg")
		{
			mScalable = true;
			retval = initSVGFromMemory((const unsigned char*)data.ptr.get(), data.length);
		}
		else
			retval = initImageFromMemory((const unsigned char*)data.ptr.get(), data.length);
	}
	return retval;
}

bool TextureData::isLoaded()
{
	std::unique_lock<std::mutex> lock(mMutex);
	if (mDataRGBA || (mTextureID != 0))
		return true;
	return false;
}

bool TextureData::uploadAndBind()
{
	// See if it's already been uploaded
	std::unique_lock<std::mutex> lock(mMutex);
	if (mTextureID != 0)
	{
		Renderer::bindTexture(mTextureID);
	}
	else
	{
		// Load it if necessary
		if (!mDataRGBA)
		{
			return false;
		}
		// Make sure we're ready to upload
		if ((mWidth == 0) || (mHeight == 0) || (mDataRGBA == nullptr))
			return false;

		// Upload texture
		mTextureID = Renderer::createTexture(Renderer::Texture::RGBA, true, mTile, (int)mWidth, (int)mHeight, mDataRGBA);
	}
	return true;
}

void TextureData::releaseVRAM()
{
	std::unique_lock<std::mutex> lock(mMutex);
	if (mTextureID != 0)
	{
		Renderer::destroyTexture(mTextureID);
		mTextureID = 0;
	}
}

void TextureData::releaseRAM()
{
	std::unique_lock<std::mutex> lock(mMutex);
	delete[] mDataRGBA;
	mDataRGBA = 0;
}

size_t TextureData::width()
{
	if (mWidth == 0)
		load();
	return mWidth;
}

size_t TextureData::height()
{
	if (mHeight == 0)
		load();
	return mHeight;
}

float TextureData::sourceWidth()
{
	if (mSourceWidth == 0)
		load();
	return mSourceWidth;
}

float TextureData::sourceHeight()
{
	if (mSourceHeight == 0)
		load();
	return mSourceHeight;
}

void TextureData::setSourceSize(float width, float height)
{
	if (mScalable)
	{
		if ((mSourceWidth != width) || (mSourceHeight != height))
		{
			mSourceWidth = width;
			mSourceHeight = height;
			releaseVRAM();
			releaseRAM();
		}
	}
}

size_t TextureData::getVRAMUsage()
{
	if ((mTextureID != 0) || (mDataRGBA != nullptr))
		return mWidth * mHeight * 4;
	else
		return 0;
}
<commit_msg>Base SVG size on height<commit_after>#include "resources/TextureData.h"

#include "math/Misc.h"
#include "renderers/Renderer.h"
#include "resources/ResourceManager.h"
#include "ImageIO.h"
#include "Log.h"
#include <nanosvg/nanosvg.h>
#include <nanosvg/nanosvgrast.h>
#include <assert.h>
#include <string.h>

#define DPI 96

TextureData::TextureData(bool tile) : mTile(tile), mTextureID(0), mDataRGBA(nullptr), mScalable(false),
									  mWidth(0), mHeight(0), mSourceWidth(0.0f), mSourceHeight(0.0f)
{
}

TextureData::~TextureData()
{
	releaseVRAM();
	releaseRAM();
}

void TextureData::initFromPath(const std::string& path)
{
	// Just set the path. It will be loaded later
	mPath = path;
	// Only textures with paths are reloadable
	mReloadable = true;
}

bool TextureData::initSVGFromMemory(const unsigned char* fileData, size_t length)
{
	// If already initialised then don't read again
	std::unique_lock<std::mutex> lock(mMutex);
	if (mDataRGBA)
		return true;

	// nsvgParse excepts a modifiable, null-terminated string
	char* copy = (char*)malloc(length + 1);
	assert(copy != NULL);
	memcpy(copy, fileData, length);
	copy[length] = '\0';

	NSVGimage* svgImage = nsvgParse(copy, "px", DPI);
	free(copy);
	if (!svgImage || (svgImage->width == 0) || (svgImage->height == 0))
	{
		LOG(LogError) << "Error parsing SVG image.";
		return false;
	}

	// We want to rasterise this texture at a specific resolution. If the source size
	// variables are set then use them otherwise set them from the parsed file
	if (mSourceHeight == 0.0f)
		mSourceHeight = svgImage->height;

	mSourceWidth = (mSourceHeight * svgImage->width) / svgImage->height;

	mWidth = (size_t)Math::round(mSourceWidth);
	mHeight = (size_t)Math::round(mSourceHeight);

	unsigned char* dataRGBA = new unsigned char[mWidth * mHeight * 4];

	NSVGrasterizer* rast = nsvgCreateRasterizer();
	float scale = Math::min(mHeight / svgImage->height, mWidth / svgImage->width);
	nsvgRasterize(rast, svgImage, 0, 0, scale, dataRGBA, (int)mWidth, (int)mHeight, (int)mWidth * 4);
	nsvgDeleteRasterizer(rast);

	ImageIO::flipPixelsVert(dataRGBA, mWidth, mHeight);

	mDataRGBA = dataRGBA;

	return true;
}

bool TextureData::initImageFromMemory(const unsigned char* fileData, size_t length)
{
	size_t width, height;

	// If already initialised then don't read again
	{
		std::unique_lock<std::mutex> lock(mMutex);
		if (mDataRGBA)
			return true;
	}

	std::vector<unsigned char> imageRGBA = ImageIO::loadFromMemoryRGBA32((const unsigned char*)(fileData), length, width, height);
	if (imageRGBA.size() == 0)
	{
		LOG(LogError) << "Could not initialize texture from memory, invalid data!  (file path: " << mPath << ", data ptr: " << (size_t)fileData << ", reported size: " << length << ")";
		return false;
	}

	mSourceWidth = (float) width;
	mSourceHeight = (float) height;
	mScalable = false;

	return initFromRGBA(imageRGBA.data(), width, height);
}

bool TextureData::initFromRGBA(const unsigned char* dataRGBA, size_t width, size_t height)
{
	// If already initialised then don't read again
	std::unique_lock<std::mutex> lock(mMutex);
	if (mDataRGBA)
		return true;

	// Take a copy
	mDataRGBA = new unsigned char[width * height * 4];
	memcpy(mDataRGBA, dataRGBA, width * height * 4);
	mWidth = width;
	mHeight = height;
	return true;
}

bool TextureData::load()
{
	bool retval = false;

	// Need to load. See if there is a file
	if (!mPath.empty())
	{
		std::shared_ptr<ResourceManager>& rm = ResourceManager::getInstance();
		const ResourceData& data = rm->getFileData(mPath);
		// is it an SVG?
		if (mPath.substr(mPath.size() - 4, std::string::npos) == ".svg")
		{
			mScalable = true;
			retval = initSVGFromMemory((const unsigned char*)data.ptr.get(), data.length);
		}
		else
			retval = initImageFromMemory((const unsigned char*)data.ptr.get(), data.length);
	}
	return retval;
}

bool TextureData::isLoaded()
{
	std::unique_lock<std::mutex> lock(mMutex);
	if (mDataRGBA || (mTextureID != 0))
		return true;
	return false;
}

bool TextureData::uploadAndBind()
{
	// See if it's already been uploaded
	std::unique_lock<std::mutex> lock(mMutex);
	if (mTextureID != 0)
	{
		Renderer::bindTexture(mTextureID);
	}
	else
	{
		// Load it if necessary
		if (!mDataRGBA)
		{
			return false;
		}
		// Make sure we're ready to upload
		if ((mWidth == 0) || (mHeight == 0) || (mDataRGBA == nullptr))
			return false;

		// Upload texture
		mTextureID = Renderer::createTexture(Renderer::Texture::RGBA, true, mTile, (int)mWidth, (int)mHeight, mDataRGBA);
	}
	return true;
}

void TextureData::releaseVRAM()
{
	std::unique_lock<std::mutex> lock(mMutex);
	if (mTextureID != 0)
	{
		Renderer::destroyTexture(mTextureID);
		mTextureID = 0;
	}
}

void TextureData::releaseRAM()
{
	std::unique_lock<std::mutex> lock(mMutex);
	delete[] mDataRGBA;
	mDataRGBA = 0;
}

size_t TextureData::width()
{
	if (mWidth == 0)
		load();
	return mWidth;
}

size_t TextureData::height()
{
	if (mHeight == 0)
		load();
	return mHeight;
}

float TextureData::sourceWidth()
{
	if (mSourceWidth == 0)
		load();
	return mSourceWidth;
}

float TextureData::sourceHeight()
{
	if (mSourceHeight == 0)
		load();
	return mSourceHeight;
}

void TextureData::setSourceSize(float width, float height)
{
	if (mScalable)
	{
		if ((mSourceWidth != width) || (mSourceHeight != height))
		{
			mSourceWidth = width;
			mSourceHeight = height;
			releaseVRAM();
			releaseRAM();
		}
	}
}

size_t TextureData::getVRAMUsage()
{
	if ((mTextureID != 0) || (mDataRGBA != nullptr))
		return mWidth * mHeight * 4;
	else
		return 0;
}
<|endoftext|>