<commit_before>/*
* 2013+ Copyright (c) Andrey Kashin <kashin.andrej@gmail.com>
* All rights reserved.
*
* This program 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 of the License, or
* (at your option) any later version.
*
* This program 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.
*/

#ifndef UPDATER_HPP
#define UPDATER_HPP

#include <stack>
#include <stdexcept>

#include "call_tree.hpp"

namespace react {

/*!
 * \brief Class for interactive building of call tree
 *
 *  Allows you to log actions in call-tree manner.
 */
class call_tree_updater_t {
public:
	/*!
	 * \brief Call tree node type
	 */
	typedef call_tree_t::node_t node_t;

	/*!
	 * \brief Pointer to call tree node type
	 */
	typedef call_tree_t::p_node_t p_node_t;

	/*!
	 * \brief Time point type
	 */
	typedef std::chrono::time_point<std::chrono::system_clock> time_point_t;

	/*!
	 * \brief Initializes updater without target tree
	 * \param max_depth Maximum monitored depth of call stack
	 */
	call_tree_updater_t(const size_t max_depth = DEFAULT_DEPTH):
		current_node(0), call_tree(NULL), depth(0), max_depth(max_depth) {
		measurements.emplace(std::chrono::system_clock::now(), +call_tree_t::NO_NODE);
	}

	/*!
	 * \brief Initializes updater with target tree
	 * \param call_tree Tree used to monitor updates
	 * \param max_depth Maximum monitored depth of call stack
	 */
	call_tree_updater_t(concurrent_call_tree_t &call_tree,
			const size_t max_depth = DEFAULT_DEPTH):
		current_node(0), call_tree(NULL), depth(0), max_depth(max_depth) {
		set_call_tree(call_tree);
		measurements.emplace(std::chrono::system_clock::now(), +call_tree_t::NO_NODE);
	}

	/*!
	 * \brief Checks if all actions were correctly finished.
	 */
	virtual ~call_tree_updater_t() {
		check_for_extra_measurements();
	}

	/*!
	 * \brief Sets target tree for updates
	 * \param call_tree Tree used to monitor updates
	 */
	void set_call_tree(concurrent_call_tree_t &call_tree) {
		check_for_extra_measurements();
		current_node = call_tree.get_call_tree().root;
		this->call_tree = &call_tree;
		depth = 0;
	}

	/*!
	 * \brief Resets current call tree
	 */
	void reset_call_tree() {
		check_for_extra_measurements();
		current_node = call_tree_t::NO_NODE;
		this->call_tree = NULL;
		depth = 0;
	}

	/*!
	 * \brief Checks whether tree for updates is set
	 * \return whether updater target tree was set
	 */
	bool has_call_tree() const {
		return (call_tree != NULL);
	}

	/*!
	 * \brief Starts new branch in tree with action \a action_code
	 * \param action_code Code of new action
	 */
	virtual void start(const int action_code) {
		start(action_code, std::chrono::system_clock::now());
	}

	/*!
	 * \brief Starts new branch in tree with action \a action_code and with specified start time
	 * \param action_code Code of new action
	 * \param start_time Action start time
	 */
	void start(const int action_code, const time_point_t& start_time) {
		++depth;
		if (get_depth() > max_depth) {
			return;
		}

		call_tree->lock();
		p_node_t next_node = call_tree->get_call_tree().add_new_link_if_missing(current_node, action_code);
		call_tree->unlock();

		measurements.emplace(start_time, current_node);
		current_node = next_node;
	}

	/*!
	 * \brief Stops last action. Updates total consumed time in call-tree.
	 * \param action_code Code of finished action
	 */
	virtual void stop(const int action_code) {
		if (get_depth() > max_depth) {
			--depth;
			return;
		}

		std::lock_guard<concurrent_call_tree_t> guard(*call_tree);

		int expected_code = call_tree->get_call_tree().get_node_action_code(current_node);
		if (expected_code != action_code) {
			std::string expected_action_name = get_action_name(expected_code);
			std::string found_action_name = get_action_name(action_code);
			throw std::logic_error("Stopping wrong action. Expected: " + expected_action_name + ", Found: " + found_action_name);
		}
		pop_measurement();
	}

	/*!
	 * \brief Sets max monitored call stack depth to \a max_depth
	 * \param max_depth Max monitored call stack depth
	 */
	void set_max_depth(const size_t max_depth) {
		if (depth != 0) {
			throw std::logic_error("can't change max_depth during update");
		}

		this->max_depth = max_depth;
	}

	/*!
	 * \brief Gets current call stack depth
	 * \return Current call stack depth
	 */
	size_t get_depth() const {
		return depth;
	}

	/*!
	 * \brief Returns name of action with \a action_code
	 * \return Name of action with \a action_code
	 */
	std::string get_action_name(int action_code) const {
		return call_tree->get_call_tree().get_actions_set().get_action_name(action_code);
	}

	/*!
	 * \brief Returns name of action in current node
	 * \return Current's node action name
	 */
	std::string get_current_node_action_name() const {
		return get_action_name(call_tree->get_call_tree().get_node_action_code(current_node));
	}

private:
	/*!
	 * \internal
	 *
	 * \brief Returns delta between two time_points
	 */
	template<class Period = std::chrono::microseconds>
	int64_t delta(const time_point_t& start, const time_point_t& end) const {
		return (std::chrono::duration_cast<Period> (end - start)).count();
	}

	/*!
	 * \internal
	 *
	 * \brief Assures that updater is empty
	 */
	void check_for_extra_measurements() {
		if (depth != 0) {
			std::string error_message;
			if (!call_tree) {
				error_message = "~time_stats_updater(): extra measurements, tree is NULL\n";
			} else {
				error_message = "~time_stats_updater(): extra measurements:\n";
				while (!measurements.empty()) {
					error_message += get_current_node_action_name() + '\n';
					pop_measurement();
				}
			}
			std::cerr << error_message << std::endl;
			throw std::logic_error(error_message);
		}
	}

	/*!
	 * \brief Represents single call measurement
	 */
	struct measurement {
		/*!
		 * \brief Initializes measurement with specified start time and pointer to previous node in call stack
		 * \param time Start time
		 * \param previous_node Pointer to previous node in call stack
		 */
		measurement(const time_point_t& time, p_node_t previous_node): start_time(time),
			previous_node(previous_node) {}

		/*!
		 * \brief Start time of the measurement
		 */
		time_point_t start_time;

		/*!
		 * \brief Pointer to previous node in call stack
		 */
		p_node_t previous_node;
	};

	/*!
	 * \brief Removes measurement from top of call stack and updates corresponding node in call-tree
	 * \param stop_time End time of the measurement
	 */
	void pop_measurement(const time_point_t& stop_time = std::chrono::system_clock::now()) {
		measurement previous_measurement = measurements.top();
		measurements.pop();
		call_tree->get_call_tree().set_node_start_time(current_node, delta(time_point_t(), previous_measurement.start_time));
		call_tree->get_call_tree().set_node_stop_time(current_node, delta(time_point_t(), stop_time));
		current_node = previous_measurement.previous_node;
		--depth;
	}

	/*!
	 * \brief Current position in call-tree
	 */
	p_node_t current_node;

	/*!
	 * \brief Call stack
	 */
	std::stack<measurement> measurements;

	/*!
	 * \brief Target call-tree
	 */
	concurrent_call_tree_t* call_tree;

	/*!
	 * \brief Current call stack depth
	 */
	size_t depth;

	/*!
	 * \brief Maximum monitored call stack depth
	 */
	size_t max_depth;

	/*!
	 * \brief Default monitored call stack depth
	 */
	static const size_t DEFAULT_DEPTH = -1;
};

class dummy_call_tree_updater_t : public call_tree_updater_t {
public:
	dummy_call_tree_updater_t() {}
	~dummy_call_tree_updater_t() {}

	void start(int) {}
	void stop(int) {}
};

/*!
 * \brief Auxiliary class for logging actions with variable place of stop time (branching/end of function/end of scope)
 */
class action_guard_t {
public:
	/*!
	 * \brief Initializes guard and starts action with \a action_code
	 * \param updater Updater whos start is called
	 * \param action_code Code of new action
	 */
	action_guard_t(call_tree_updater_t *updater, const int action_code):
		updater(updater), action_code(action_code), is_stopped(false) {
		if (updater) {
			updater->start(action_code);
		}
	}

	/*!
	 * \brief Stops action if it is not already stopped
	 */
	~action_guard_t() {
		if (!is_stopped && updater) {
			updater->stop(action_code);
		}
	}

	/*!
	 * \brief Allows to stop action manually
	 */
	void stop() {
		if (is_stopped) {
			throw std::logic_error("action " + updater->get_action_name(action_code) + " is already stopped");
		}

		if (updater) {
			updater->stop(action_code);
			is_stopped = true;
		}
	}

private:
	/*!
	 * \brief Updater whos start/stop are called
	 */
	call_tree_updater_t *updater;

	/*!
	 * \brief Action code of guarded action
	 */
	const int action_code;

	/*!
	 * \brief Shows if action is already stopped
	 */
	bool is_stopped;
};

} // namespace react

#endif // UPDATER_HPP
<commit_msg>core: updater<commit_after>/*
* 2013+ Copyright (c) Andrey Kashin <kashin.andrej@gmail.com>
* All rights reserved.
*
* This program 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 of the License, or
* (at your option) any later version.
*
* This program 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.
*/

#ifndef UPDATER_HPP
#define UPDATER_HPP

#include <stack>
#include <stdexcept>

#include "call_tree.hpp"

namespace react {

/*!
 * \brief Class for interactive building of call tree
 *
 *  Allows you to log actions in call-tree manner.
 */
class call_tree_updater_t {
public:
	/*!
	 * \brief Call tree node type
	 */
	typedef call_tree_t::node_t node_t;

	/*!
	 * \brief Pointer to call tree node type
	 */
	typedef call_tree_t::p_node_t p_node_t;

	/*!
	 * \brief Time point type
	 */
	typedef std::chrono::time_point<std::chrono::system_clock> time_point_t;

	/*!
	 * \brief Default monitored call stack depth
	 */
	static const size_t DEFAULT_MAX_TRACE_DEPTH = -1;

	/*!
	 * \brief Initializes updater without target tree
	 * \param max_depth Maximum monitored depth of call stack
	 */
	call_tree_updater_t(const size_t max_depth = DEFAULT_MAX_TRACE_DEPTH):
		current_node(+call_tree_t::NO_NODE), call_tree(NULL),
		trace_depth(0), max_trace_depth(max_depth) {
		measurements.emplace(std::chrono::system_clock::now(), +call_tree_t::NO_NODE);
	}

	/*!
	 * \brief Initializes updater with target tree
	 * \param call_tree Tree used to monitor updates
	 * \param max_depth Maximum monitored depth of call stack
	 */
	call_tree_updater_t(concurrent_call_tree_t &call_tree,
			const size_t max_depth = DEFAULT_MAX_TRACE_DEPTH):
		current_node(+call_tree_t::NO_NODE), call_tree(NULL),
		trace_depth(0), max_trace_depth(max_depth) {
		set_call_tree(call_tree);
		measurements.emplace(std::chrono::system_clock::now(), +call_tree_t::NO_NODE);
	}

	/*!
	 * \brief Checks if all actions were correctly finished.
	 */
	~call_tree_updater_t() {
		try {
			check_for_extra_measurements();
		} catch (std::logic_error &e) {
			std::cerr << e.what() << std::endl;
		}
	}

	/*!
	 * \brief Sets target tree for updates
	 * \param call_tree Tree used to monitor updates
	 */
	void set_call_tree(concurrent_call_tree_t &call_tree) {
		check_for_extra_measurements();
		current_node = call_tree.get_call_tree().root;
		this->call_tree = &call_tree;
		trace_depth = 0;
	}

	/*!
	 * \brief Resets current call tree
	 */
	void reset_call_tree() {
		check_for_extra_measurements();
		current_node = call_tree_t::NO_NODE;
		this->call_tree = NULL;
		trace_depth = 0;
	}

	/*!
	 * \brief Checks whether tree for updates is set
	 * \return whether updater target tree was set
	 */
	bool has_call_tree() const {
		return (call_tree != NULL);
	}

	/*!
	 * \brief Starts new branch in tree with action \a action_code
	 * \param action_code Code of new action
	 */
	void start(const int action_code) {
		start(action_code, std::chrono::system_clock::now());
	}

	/*!
	 * \brief Starts new branch in tree with action \a action_code and with specified start time
	 * \param action_code Code of new action
	 * \param start_time Action start time
	 */
	void start(const int action_code, const time_point_t& start_time) {
		if (!action_code_is_valid(action_code)) {
			throw std::invalid_argument(
						"Can't start action: action code is invalid: " + std::to_string(action_code)
						);
		}

		++trace_depth;
		if (get_trace_depth() > max_trace_depth) {
			return;
		}

		p_node_t next_node = call_tree_t::NO_NODE;
		{
			std::lock_guard<concurrent_call_tree_t> guard(*call_tree);
			next_node = call_tree->get_call_tree().add_new_link(current_node, action_code);
		}

		measurements.emplace(start_time, current_node);
		current_node = next_node;
	}

	/*!
	 * \brief Stops last action. Updates total consumed time in call-tree.
	 * \param action_code Code of finished action
	 */
	void stop(const int action_code) {
		if (!action_code_is_valid(action_code)) {
			throw std::invalid_argument(
						"Can't stop action: action code is invalid: " + std::to_string(action_code)
						);
		}

		if (get_trace_depth() > max_trace_depth) {
			--trace_depth;
			return;
		}

		std::lock_guard<concurrent_call_tree_t> guard(*call_tree);

		int expected_code = call_tree->get_call_tree().get_node_action_code(current_node);
		if (expected_code != action_code) {
			std::string expected_action_name = get_action_name(expected_code);
			std::string found_action_name = get_action_name(action_code);
			throw std::logic_error("Stopping wrong action. Expected: " + expected_action_name + ", Found: " + found_action_name);
		}
		pop_measurement();
	}

	/*!
	 * \brief Gets max allowed call stack depth
	 * \return Max allowed call stack depth
	 */
	size_t get_max_trace_depth() const {
		return max_trace_depth;
	}

	/*!
	 * \brief Sets max monitored call stack depth to \a max_depth
	 * \param max_depth Max monitored call stack depth
	 */
	void set_max_trace_depth(const size_t max_depth) {
		if (trace_depth != 0) {
			throw std::logic_error("can't change max_depth during update");
		}

		this->max_trace_depth = max_depth;
	}

	/*!
	 * \brief Gets current call stack depth
	 * \return Current call stack depth
	 */
	size_t get_actual_trace_depth() const {
		return measurements.size() - 1;
	}

	/*!
	 * \brief Gets current intended call stack depth
	 * \return Current intended call stack depth
	 */
	size_t get_trace_depth() const {
		return trace_depth;
	}

	/*!
	 * \brief Returns name of action with \a action_code
	 * \return Name of action with \a action_code
	 */
	std::string get_action_name(int action_code) const {
		if (!has_call_tree()) {
			throw std::logic_error("Can't get action name: tree is not set");
		}

		return call_tree->get_call_tree().get_actions_set().get_action_name(action_code);
	}

	/*!
	 * \brief Checks whether \a action_code is registred in actions_set
	 * \return True if \a action_code is registred, false otherwise
	 */
	bool action_code_is_valid(int action_code) {
		if (!has_call_tree()) {
			throw std::logic_error("Can't check action_code validity: tree is not set");
		}

		return call_tree->get_call_tree().get_actions_set().code_is_valid(action_code);
	}

	/*!
	 * \brief Returns updater's current node or NO_NODE if call tree is not set
	 * \return Pointer to updater's current node
	 */
	p_node_t get_current_node() const {
		return current_node;
	}

	/*!
	 * \brief Returns name of action in current node
	 * \return Current's node action name
	 */
	std::string get_current_node_action_name() const {
		if (!has_call_tree()) {
			throw std::logic_error("Can't get action name: tree is not set");
		}

		return get_action_name(call_tree->get_call_tree().get_node_action_code(current_node));
	}

private:
	/*!
	 * \internal
	 *
	 * \brief Returns delta between two time_points
	 */
	template<class Period = std::chrono::microseconds>
	int64_t delta(const time_point_t& start, const time_point_t& end) const {
		return (std::chrono::duration_cast<Period> (end - start)).count();
	}

	/*!
	 * \internal
	 *
	 * \brief Assures that updater is empty
	 */
	void check_for_extra_measurements() {
		if (get_trace_depth() != 0) {
			std::string error_message;
			if (!call_tree) {
				error_message = "~time_stats_updater(): extra measurements, tree is NULL\n";
			} else {
				error_message = "~time_stats_updater(): extra measurements:\n";
				if (get_actual_trace_depth() != get_trace_depth()) {
					error_message +=
							std::to_string(get_trace_depth() - get_actual_trace_depth())
							+ " untracked actions due to max depth\n";
				}
				while (get_actual_trace_depth() > 0) {
					error_message += get_current_node_action_name() + '\n';
					pop_measurement();
				}
			}
			throw std::logic_error(error_message);
		}
	}

	/*!
	 * \brief Represents single call measurement
	 */
	struct measurement {
		/*!
		 * \brief Initializes measurement with specified start time and pointer to previous node in call stack
		 * \param time Start time
		 * \param previous_node Pointer to previous node in call stack
		 */
		measurement(const time_point_t& time, p_node_t previous_node): start_time(time),
			previous_node(previous_node) {}

		/*!
		 * \brief Start time of the measurement
		 */
		time_point_t start_time;

		/*!
		 * \brief Pointer to previous node in call stack
		 */
		p_node_t previous_node;
	};

	/*!
	 * \brief Removes measurement from top of call stack and updates corresponding node in call-tree
	 * \param stop_time End time of the measurement
	 */
	void pop_measurement(const time_point_t& stop_time = std::chrono::system_clock::now()) {
		measurement previous_measurement = measurements.top();
		measurements.pop();
		call_tree->get_call_tree().set_node_start_time(current_node, delta(time_point_t(), previous_measurement.start_time));
		call_tree->get_call_tree().set_node_stop_time(current_node, delta(time_point_t(), stop_time));
		current_node = previous_measurement.previous_node;
		--trace_depth;
	}

	/*!
	 * \brief Current position in call-tree
	 */
	p_node_t current_node;

	/*!
	 * \brief Call stack
	 */
	std::stack<measurement> measurements;

	/*!
	 * \brief Target call-tree
	 */
	concurrent_call_tree_t* call_tree;

	/*!
	 * \brief Current call stack depth
	 */
	size_t trace_depth;

	/*!
	 * \brief Maximum monitored call stack depth
	 */
	size_t max_trace_depth;
};

/*!
 * \brief Auxiliary class for logging actions with variable place of stop time (branching/end of function/end of scope)
 */
class action_guard_t {
public:
	/*!
	 * \brief Initializes guard and starts action with \a action_code
	 * \param updater Updater whos start is called
	 * \param action_code Code of new action
	 */
	action_guard_t(call_tree_updater_t *updater, const int action_code):
		updater(updater), action_code(action_code), is_stopped(false) {
		if (updater) {
			updater->start(action_code);
		}
	}

	/*!
	 * \brief Stops action if it is not already stopped
	 */
	~action_guard_t() {
		if (!is_stopped && updater) {
			updater->stop(action_code);
		}
	}

	/*!
	 * \brief Allows to stop action manually
	 */
	void stop() {
		if (is_stopped) {
			std::string error_message;

			if (updater) {
				error_message = "action "
						+ updater->get_action_name(action_code)
						+ " is already stopped";
			} else {
				error_message = "action "
						+ std::to_string(action_code)
						+ " is already stopped";
			}

			throw std::logic_error(error_message);
		}

		if (updater) {
			updater->stop(action_code);
		}
		is_stopped = true;
	}

private:
	/*!
	 * \brief Updater whos start/stop are called
	 */
	call_tree_updater_t *updater;

	/*!
	 * \brief Action code of guarded action
	 */
	const int action_code;

	/*!
	 * \brief Shows if action is already stopped
	 */
	bool is_stopped;
};

} // namespace react

#endif // UPDATER_HPP
<|endoftext|>


<commit_before>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*************************************************************************
 *
 * 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.
 *
 ************************************************************************/

#define UNICODE
#define _UNICODE

#include <cstddef>

#define WIN32_LEAN_AND_MEAN
#if defined _MSC_VER
#pragma warning(push, 1)
#endif
#include <windows.h>
#include <shellapi.h>
#if defined _MSC_VER
#pragma warning(pop)
#endif

#include <tchar.h>

#include <malloc.h>
#include <string.h>
#include <stdlib.h>
#include <systools/win32/uwinapi.h>

#include "rtl/string.h"
#include <sal/macros.h>

#include "../../../source/inc/exithelper.hxx"
#include "../extendloaderenvironment.hxx"

#define PIPE_PREFIX                 TEXT("\\\\.\\pipe\\OSL_PIPE_")
#define PIPE_POSTFIX                TEXT("_SingleOfficeIPC_")
#define PIPE_TERMINATION_SEQUENCE   "InternalIPC::ProcessingDone"

BOOL WINAPI ConvertSidToStringSid( PSID pSid, LPTSTR* StringSid )
{
    PSID_IDENTIFIER_AUTHORITY psia;
    DWORD dwSubAuthorities;
    DWORD dwSidRev=SID_REVISION;
    DWORD dwCounter;
    DWORD dwSidSize;

    // Validate the binary SID.

    if(!IsValidSid(pSid)) return FALSE;

    // Get the identifier authority value from the SID.

    psia = GetSidIdentifierAuthority(pSid);

    // Get the number of subauthorities in the SID.

    dwSubAuthorities = *GetSidSubAuthorityCount(pSid);

    // Compute the buffer length.
    // S-SID_REVISION- + IdentifierAuthority- + subauthorities- + NULL

    dwSidSize=(15 + 12 + (12 * dwSubAuthorities) + 1) * sizeof(TCHAR);

    *StringSid = (LPTSTR)LocalAlloc( LMEM_FIXED, dwSidSize );

    // Add 'S' prefix and revision number to the string.

    dwSidSize=wsprintf(*StringSid, TEXT("S-%lu-"), dwSidRev );

    // Add a SID identifier authority to the string.

    if ( (psia->Value[0] != 0) || (psia->Value[1] != 0) )
    {
        dwSidSize+=wsprintf(*StringSid + lstrlen(*StringSid),
                    TEXT("0x%02hx%02hx%02hx%02hx%02hx%02hx"),
                    (USHORT)psia->Value[0],
                    (USHORT)psia->Value[1],
                    (USHORT)psia->Value[2],
                    (USHORT)psia->Value[3],
                    (USHORT)psia->Value[4],
                    (USHORT)psia->Value[5]);
    }
    else
    {
        dwSidSize+=wsprintf(*StringSid + lstrlen(*StringSid),
                    TEXT("%lu"),
                    (ULONG)(psia->Value[5]      )   +
                    (ULONG)(psia->Value[4] <<  8)   +
                    (ULONG)(psia->Value[3] << 16)   +
                    (ULONG)(psia->Value[2] << 24)   );
    }

    // Add SID subauthorities to the string.
    for (dwCounter=0 ; dwCounter < dwSubAuthorities ; dwCounter++)
    {
        dwSidSize+=wsprintf(*StringSid + dwSidSize, TEXT("-%lu"),
                    *GetSidSubAuthority(pSid, dwCounter) );
    }

    return TRUE;
}

//---------------------------------------------------------------------------

static LPTSTR   *GetCommandArgs( int *pArgc )
{
#ifdef UNICODE
    return CommandLineToArgvW( GetCommandLineW(), pArgc );
#else
    *pArgc = __argc;
    return __argv;
#endif
}

//---------------------------------------------------------------------------

namespace {

bool writeArgument(HANDLE pipe, char prefix, WCHAR const * argument) {
    CHAR szBuffer[4096];
    int n = WideCharToMultiByte(
        CP_UTF8, 0, argument, -1, szBuffer, sizeof (szBuffer), NULL, NULL);
    char b[1 + 2 * ((sizeof szBuffer) - 1)]; // hopefully does not overflow
    b[0] = prefix;
    char * p = b + 1;
    for (int i = 0; i < n - 1; ++i) { // cannot underflow (n >= 0)
        char c = szBuffer[i];
        switch (c) {
        case '\0':
            *p++ = '\\';
            *p++ = '0';
            break;
        case ',':
            *p++ = '\\';
            *p++ = ',';
            break;
        case '\\':
            *p++ = '\\';
            *p++ = '\\';
            break;
        default:
            *p++ = c;
            break;
        }
    }
    DWORD w;
    return WriteFile(pipe, b, p - b, &w, NULL);
}

}

#ifdef __MINGW32__
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE, LPSTR, int )
#else
int WINAPI _tWinMain( HINSTANCE, HINSTANCE, LPTSTR, int )
#endif
{
    TCHAR               szTargetFileName[MAX_PATH] = TEXT("");
    TCHAR               szIniDirectory[MAX_PATH];
    TCHAR               szPerfTuneIniFile[MAX_PATH] = TEXT("");
    STARTUPINFO         aStartupInfo;

    desktop_win32::extendLoaderEnvironment(szTargetFileName, szIniDirectory);

    ZeroMemory( &aStartupInfo, sizeof(aStartupInfo) );
    aStartupInfo.cb = sizeof(aStartupInfo);

    GetStartupInfo( &aStartupInfo );
    // Get image path with same name but with .bin extension

    TCHAR               szModuleFileName[MAX_PATH];

    GetModuleFileName( NULL, szModuleFileName, MAX_PATH );
    _TCHAR  *lpLastSlash = _tcsrchr( szModuleFileName, '\\' );
    if ( lpLastSlash )
    {
        size_t len = lpLastSlash - szModuleFileName + 1;
        _tcsncpy( szPerfTuneIniFile, szModuleFileName, len );
        _tcsncpy( szPerfTuneIniFile + len, _T("perftune.ini"), SAL_N_ELEMENTS(szPerfTuneIniFile) - len );
    }

    // Create process with same command line, environment and stdio handles which
    // are directed to the created pipes

    DWORD   dwExitCode = (DWORD)-1;

    BOOL    fSuccess = FALSE;
    LPTSTR  lpCommandLine = NULL;
    int argc = 0;
    LPTSTR * argv = NULL;
    bool bFirst = true;
    WCHAR cwd[MAX_PATH];
    DWORD cwdLen = GetCurrentDirectoryW(MAX_PATH, cwd);
    if (cwdLen >= MAX_PATH) {
        cwdLen = 0;
    }

    do
    {
        TCHAR   szKey[32];

        GetPrivateProfileString(
            TEXT("PerformanceTuning"),
            TEXT("FastPipeCommunication"),
            TEXT("0"),
            szKey,
            SAL_N_ELEMENTS(szKey),
            szPerfTuneIniFile
            );

        if ( 0 == _tcscmp( szKey, TEXT("1") ) )
        {
            HANDLE  hProcessToken;

            if ( OpenProcessToken( GetCurrentProcess(), TOKEN_QUERY, &hProcessToken ) )
            {
                TCHAR   szPipeName[4096];

                DWORD   dwTokenLength = 0;

                fSuccess = GetTokenInformation( hProcessToken, TokenUser, NULL, dwTokenLength, &dwTokenLength );

                PVOID   pTokenInfo = _alloca(dwTokenLength);
                fSuccess = GetTokenInformation( hProcessToken, TokenUser, pTokenInfo, dwTokenLength, &dwTokenLength );
                CloseHandle( hProcessToken );

                PSID pSid = ((PTOKEN_USER)pTokenInfo)->User.Sid;
                LPTSTR  szUserIdent = NULL;
                TCHAR   szSUPD[11] = TEXT("0");

                fSuccess = ConvertSidToStringSid( pSid, &szUserIdent );

                _tcsncpy( szPipeName, PIPE_PREFIX, SAL_N_ELEMENTS(szPipeName) );
                _tcsncat( szPipeName, szUserIdent, SAL_N_ELEMENTS(szPipeName) - _tcslen(szPipeName) - 1 );
                _tcsncat( szPipeName, PIPE_POSTFIX, SAL_N_ELEMENTS(szPipeName) - _tcslen(szPipeName) - 1 );
                _tcsncat( szPipeName, _ultot( SUPD, szSUPD, 10), SAL_N_ELEMENTS(szPipeName) - _tcslen(szPipeName) - 1 );

                LocalFree( szUserIdent );

                HANDLE  hPipe = CreateFile(
                                    szPipeName,
                                    GENERIC_READ|GENERIC_WRITE,
                                    FILE_SHARE_READ | FILE_SHARE_WRITE,
                                    NULL,
                                    OPEN_EXISTING,
                                    FILE_ATTRIBUTE_NORMAL,
                                    NULL);

                if ( INVALID_HANDLE_VALUE != hPipe )
                {
                    DWORD   dwBytesWritten;
                    int argc = 0;
                    LPWSTR  *argv = CommandLineToArgvW( GetCommandLine(), &argc );

                    fSuccess = WriteFile( hPipe, RTL_CONSTASCII_STRINGPARAM("InternalIPC::Arguments"), &dwBytesWritten, NULL );
                    if (fSuccess) {
                        if (cwdLen > 0) {
                            fSuccess = writeArgument(hPipe, '2', cwd);
                        } else {
                            fSuccess = WriteFile(
                                hPipe, RTL_CONSTASCII_STRINGPARAM("0"),
                                &dwBytesWritten, NULL);
                        }
                    }
                    for ( int argn = 1; fSuccess && argn < argc; argn++ )
                    {
                        fSuccess = writeArgument(hPipe, ',', argv[argn]);
                    }

                    if ( fSuccess )
                    {
                        fSuccess = WriteFile(  hPipe, "", 1, &dwBytesWritten, NULL );
                        if ( fSuccess )
                        {
                            DWORD   dwBytesRead = 0;
                            char    *pBuffer = (char *)_alloca( sizeof(PIPE_TERMINATION_SEQUENCE) );
                            fSuccess = ReadFile( hPipe, pBuffer, sizeof(PIPE_TERMINATION_SEQUENCE) - 1, &dwBytesRead, NULL );
                            if ( fSuccess )
                            {
                                pBuffer[dwBytesRead] = 0;
                                if ( 0 != strcmp( PIPE_TERMINATION_SEQUENCE, pBuffer ) )
                                    fSuccess = FALSE;
                            }
                        }
                    }

                    CloseHandle( hPipe );

                    return fSuccess ? 0 : -1;
                }

            }
        }

        if ( bFirst ) {
            argv = GetCommandArgs(&argc);
            std::size_t n = wcslen(argv[0]) + 2;
            for (int i = 1; i < argc; ++i) {
                n += wcslen(argv[i]) + 3;
            }
            n += MY_LENGTH(L" \"-env:OOO_CWD=2") + 4 * cwdLen +
                MY_LENGTH(L"\"") + 1;
                // 4 * cwdLen: each char preceded by backslash, each trailing
                // backslash doubled
            lpCommandLine = new WCHAR[n];
        }
        WCHAR * p = desktop_win32::commandLineAppend(
            lpCommandLine, MY_STRING(L"\""));
        p = desktop_win32::commandLineAppend(p, argv[0]);
        for (int i = 1; i < argc; ++i) {
            if (bFirst || ::desktop::ExitHelper::E_NORMAL_RESTART == dwExitCode || wcsncmp(argv[i], MY_STRING(L"-env:")) == 0) {
                p = desktop_win32::commandLineAppend(p, MY_STRING(L"\" \""));
                p = desktop_win32::commandLineAppend(p, argv[i]);
            }
        }

        p = desktop_win32::commandLineAppend(
            p, MY_STRING(L"\" \"-env:OOO_CWD="));
        if (cwdLen == 0) {
            p = desktop_win32::commandLineAppend(p, MY_STRING(L"0"));
        } else {
            p = desktop_win32::commandLineAppend(p, MY_STRING(L"2"));
            p = desktop_win32::commandLineAppendEncoded(p, cwd);
        }
        desktop_win32::commandLineAppend(p, MY_STRING(L"\""));
        bFirst = false;

        TCHAR   szParentProcessId[64]; // This is more than large enough for a 128 bit decimal value
        BOOL    bHeadlessMode( FALSE );

        {
            // Check command line arguments for "--headless" parameter. We only
            // set the environment variable "ATTACHED_PARENT_PROCESSID" for the headless
            // mode as self-destruction of the soffice.bin process can lead to
            // certain side-effects (log-off can result in data-loss, ".lock" is not deleted.
            // See 138244 for more information.
            int     argc;
            LPTSTR  *argv = GetCommandArgs( &argc );

            if ( argc > 1 )
            {
                int n;

                for ( n = 1; n < argc; n++ )
                {
                    if ( 0 == _tcsnicmp( argv[n], _T("-headless"), 9 ) ||
                         0 == _tcsnicmp( argv[n], _T("--headless"), 9 ) )
                    {
                        bHeadlessMode = TRUE;
                    }
                }
            }
        }

        if ( _ltot( (long)GetCurrentProcessId(),szParentProcessId, 10 ) && bHeadlessMode )
            SetEnvironmentVariable( TEXT("ATTACHED_PARENT_PROCESSID"), szParentProcessId );

        PROCESS_INFORMATION aProcessInfo;

        fSuccess = CreateProcess(
            szTargetFileName,
            lpCommandLine,
            NULL,
            NULL,
            TRUE,
            0,
            NULL,
            szIniDirectory,
            &aStartupInfo,
            &aProcessInfo );

        if ( fSuccess )
        {
            DWORD   dwWaitResult;

            do
            {
                // On Windows XP it seems as the desktop calls WaitForInputIdle after "OpenWidth" so we have to do so
                // as if we where processing any messages

                dwWaitResult = MsgWaitForMultipleObjects( 1, &aProcessInfo.hProcess, FALSE, INFINITE, QS_ALLEVENTS );

                if (  WAIT_OBJECT_0 + 1 == dwWaitResult )
                {
                    MSG msg;

                    PeekMessage( &msg, NULL, 0, 0, PM_REMOVE );
                }
            } while ( WAIT_OBJECT_0 + 1 == dwWaitResult );

            dwExitCode = 0;
            GetExitCodeProcess( aProcessInfo.hProcess, &dwExitCode );

            CloseHandle( aProcessInfo.hProcess );
            CloseHandle( aProcessInfo.hThread );
        }
    } while ( false );
    delete[] lpCommandLine;

    return fSuccess ? dwExitCode : -1;
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<commit_msg>WaE: hInstance arg unused under mingw32<commit_after>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*************************************************************************
 *
 * 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.
 *
 ************************************************************************/

#define UNICODE
#define _UNICODE

#include <cstddef>

#define WIN32_LEAN_AND_MEAN
#if defined _MSC_VER
#pragma warning(push, 1)
#endif
#include <windows.h>
#include <shellapi.h>
#if defined _MSC_VER
#pragma warning(pop)
#endif

#include <tchar.h>

#include <malloc.h>
#include <string.h>
#include <stdlib.h>
#include <systools/win32/uwinapi.h>

#include "rtl/string.h"
#include <sal/macros.h>

#include "../../../source/inc/exithelper.hxx"
#include "../extendloaderenvironment.hxx"

#define PIPE_PREFIX                 TEXT("\\\\.\\pipe\\OSL_PIPE_")
#define PIPE_POSTFIX                TEXT("_SingleOfficeIPC_")
#define PIPE_TERMINATION_SEQUENCE   "InternalIPC::ProcessingDone"

BOOL WINAPI ConvertSidToStringSid( PSID pSid, LPTSTR* StringSid )
{
    PSID_IDENTIFIER_AUTHORITY psia;
    DWORD dwSubAuthorities;
    DWORD dwSidRev=SID_REVISION;
    DWORD dwCounter;
    DWORD dwSidSize;

    // Validate the binary SID.

    if(!IsValidSid(pSid)) return FALSE;

    // Get the identifier authority value from the SID.

    psia = GetSidIdentifierAuthority(pSid);

    // Get the number of subauthorities in the SID.

    dwSubAuthorities = *GetSidSubAuthorityCount(pSid);

    // Compute the buffer length.
    // S-SID_REVISION- + IdentifierAuthority- + subauthorities- + NULL

    dwSidSize=(15 + 12 + (12 * dwSubAuthorities) + 1) * sizeof(TCHAR);

    *StringSid = (LPTSTR)LocalAlloc( LMEM_FIXED, dwSidSize );

    // Add 'S' prefix and revision number to the string.

    dwSidSize=wsprintf(*StringSid, TEXT("S-%lu-"), dwSidRev );

    // Add a SID identifier authority to the string.

    if ( (psia->Value[0] != 0) || (psia->Value[1] != 0) )
    {
        dwSidSize+=wsprintf(*StringSid + lstrlen(*StringSid),
                    TEXT("0x%02hx%02hx%02hx%02hx%02hx%02hx"),
                    (USHORT)psia->Value[0],
                    (USHORT)psia->Value[1],
                    (USHORT)psia->Value[2],
                    (USHORT)psia->Value[3],
                    (USHORT)psia->Value[4],
                    (USHORT)psia->Value[5]);
    }
    else
    {
        dwSidSize+=wsprintf(*StringSid + lstrlen(*StringSid),
                    TEXT("%lu"),
                    (ULONG)(psia->Value[5]      )   +
                    (ULONG)(psia->Value[4] <<  8)   +
                    (ULONG)(psia->Value[3] << 16)   +
                    (ULONG)(psia->Value[2] << 24)   );
    }

    // Add SID subauthorities to the string.
    for (dwCounter=0 ; dwCounter < dwSubAuthorities ; dwCounter++)
    {
        dwSidSize+=wsprintf(*StringSid + dwSidSize, TEXT("-%lu"),
                    *GetSidSubAuthority(pSid, dwCounter) );
    }

    return TRUE;
}

//---------------------------------------------------------------------------

static LPTSTR   *GetCommandArgs( int *pArgc )
{
#ifdef UNICODE
    return CommandLineToArgvW( GetCommandLineW(), pArgc );
#else
    *pArgc = __argc;
    return __argv;
#endif
}

//---------------------------------------------------------------------------

namespace {

bool writeArgument(HANDLE pipe, char prefix, WCHAR const * argument) {
    CHAR szBuffer[4096];
    int n = WideCharToMultiByte(
        CP_UTF8, 0, argument, -1, szBuffer, sizeof (szBuffer), NULL, NULL);
    char b[1 + 2 * ((sizeof szBuffer) - 1)]; // hopefully does not overflow
    b[0] = prefix;
    char * p = b + 1;
    for (int i = 0; i < n - 1; ++i) { // cannot underflow (n >= 0)
        char c = szBuffer[i];
        switch (c) {
        case '\0':
            *p++ = '\\';
            *p++ = '0';
            break;
        case ',':
            *p++ = '\\';
            *p++ = ',';
            break;
        case '\\':
            *p++ = '\\';
            *p++ = '\\';
            break;
        default:
            *p++ = c;
            break;
        }
    }
    DWORD w;
    return WriteFile(pipe, b, p - b, &w, NULL);
}

}

#ifdef __MINGW32__
int WINAPI WinMain( HINSTANCE, HINSTANCE, LPSTR, int )
#else
int WINAPI _tWinMain( HINSTANCE, HINSTANCE, LPTSTR, int )
#endif
{
    TCHAR               szTargetFileName[MAX_PATH] = TEXT("");
    TCHAR               szIniDirectory[MAX_PATH];
    TCHAR               szPerfTuneIniFile[MAX_PATH] = TEXT("");
    STARTUPINFO         aStartupInfo;

    desktop_win32::extendLoaderEnvironment(szTargetFileName, szIniDirectory);

    ZeroMemory( &aStartupInfo, sizeof(aStartupInfo) );
    aStartupInfo.cb = sizeof(aStartupInfo);

    GetStartupInfo( &aStartupInfo );
    // Get image path with same name but with .bin extension

    TCHAR               szModuleFileName[MAX_PATH];

    GetModuleFileName( NULL, szModuleFileName, MAX_PATH );
    _TCHAR  *lpLastSlash = _tcsrchr( szModuleFileName, '\\' );
    if ( lpLastSlash )
    {
        size_t len = lpLastSlash - szModuleFileName + 1;
        _tcsncpy( szPerfTuneIniFile, szModuleFileName, len );
        _tcsncpy( szPerfTuneIniFile + len, _T("perftune.ini"), SAL_N_ELEMENTS(szPerfTuneIniFile) - len );
    }

    // Create process with same command line, environment and stdio handles which
    // are directed to the created pipes

    DWORD   dwExitCode = (DWORD)-1;

    BOOL    fSuccess = FALSE;
    LPTSTR  lpCommandLine = NULL;
    int argc = 0;
    LPTSTR * argv = NULL;
    bool bFirst = true;
    WCHAR cwd[MAX_PATH];
    DWORD cwdLen = GetCurrentDirectoryW(MAX_PATH, cwd);
    if (cwdLen >= MAX_PATH) {
        cwdLen = 0;
    }

    do
    {
        TCHAR   szKey[32];

        GetPrivateProfileString(
            TEXT("PerformanceTuning"),
            TEXT("FastPipeCommunication"),
            TEXT("0"),
            szKey,
            SAL_N_ELEMENTS(szKey),
            szPerfTuneIniFile
            );

        if ( 0 == _tcscmp( szKey, TEXT("1") ) )
        {
            HANDLE  hProcessToken;

            if ( OpenProcessToken( GetCurrentProcess(), TOKEN_QUERY, &hProcessToken ) )
            {
                TCHAR   szPipeName[4096];

                DWORD   dwTokenLength = 0;

                fSuccess = GetTokenInformation( hProcessToken, TokenUser, NULL, dwTokenLength, &dwTokenLength );

                PVOID   pTokenInfo = _alloca(dwTokenLength);
                fSuccess = GetTokenInformation( hProcessToken, TokenUser, pTokenInfo, dwTokenLength, &dwTokenLength );
                CloseHandle( hProcessToken );

                PSID pSid = ((PTOKEN_USER)pTokenInfo)->User.Sid;
                LPTSTR  szUserIdent = NULL;
                TCHAR   szSUPD[11] = TEXT("0");

                fSuccess = ConvertSidToStringSid( pSid, &szUserIdent );

                _tcsncpy( szPipeName, PIPE_PREFIX, SAL_N_ELEMENTS(szPipeName) );
                _tcsncat( szPipeName, szUserIdent, SAL_N_ELEMENTS(szPipeName) - _tcslen(szPipeName) - 1 );
                _tcsncat( szPipeName, PIPE_POSTFIX, SAL_N_ELEMENTS(szPipeName) - _tcslen(szPipeName) - 1 );
                _tcsncat( szPipeName, _ultot( SUPD, szSUPD, 10), SAL_N_ELEMENTS(szPipeName) - _tcslen(szPipeName) - 1 );

                LocalFree( szUserIdent );

                HANDLE  hPipe = CreateFile(
                                    szPipeName,
                                    GENERIC_READ|GENERIC_WRITE,
                                    FILE_SHARE_READ | FILE_SHARE_WRITE,
                                    NULL,
                                    OPEN_EXISTING,
                                    FILE_ATTRIBUTE_NORMAL,
                                    NULL);

                if ( INVALID_HANDLE_VALUE != hPipe )
                {
                    DWORD   dwBytesWritten;
                    int argc = 0;
                    LPWSTR  *argv = CommandLineToArgvW( GetCommandLine(), &argc );

                    fSuccess = WriteFile( hPipe, RTL_CONSTASCII_STRINGPARAM("InternalIPC::Arguments"), &dwBytesWritten, NULL );
                    if (fSuccess) {
                        if (cwdLen > 0) {
                            fSuccess = writeArgument(hPipe, '2', cwd);
                        } else {
                            fSuccess = WriteFile(
                                hPipe, RTL_CONSTASCII_STRINGPARAM("0"),
                                &dwBytesWritten, NULL);
                        }
                    }
                    for ( int argn = 1; fSuccess && argn < argc; argn++ )
                    {
                        fSuccess = writeArgument(hPipe, ',', argv[argn]);
                    }

                    if ( fSuccess )
                    {
                        fSuccess = WriteFile(  hPipe, "", 1, &dwBytesWritten, NULL );
                        if ( fSuccess )
                        {
                            DWORD   dwBytesRead = 0;
                            char    *pBuffer = (char *)_alloca( sizeof(PIPE_TERMINATION_SEQUENCE) );
                            fSuccess = ReadFile( hPipe, pBuffer, sizeof(PIPE_TERMINATION_SEQUENCE) - 1, &dwBytesRead, NULL );
                            if ( fSuccess )
                            {
                                pBuffer[dwBytesRead] = 0;
                                if ( 0 != strcmp( PIPE_TERMINATION_SEQUENCE, pBuffer ) )
                                    fSuccess = FALSE;
                            }
                        }
                    }

                    CloseHandle( hPipe );

                    return fSuccess ? 0 : -1;
                }

            }
        }

        if ( bFirst ) {
            argv = GetCommandArgs(&argc);
            std::size_t n = wcslen(argv[0]) + 2;
            for (int i = 1; i < argc; ++i) {
                n += wcslen(argv[i]) + 3;
            }
            n += MY_LENGTH(L" \"-env:OOO_CWD=2") + 4 * cwdLen +
                MY_LENGTH(L"\"") + 1;
                // 4 * cwdLen: each char preceded by backslash, each trailing
                // backslash doubled
            lpCommandLine = new WCHAR[n];
        }
        WCHAR * p = desktop_win32::commandLineAppend(
            lpCommandLine, MY_STRING(L"\""));
        p = desktop_win32::commandLineAppend(p, argv[0]);
        for (int i = 1; i < argc; ++i) {
            if (bFirst || ::desktop::ExitHelper::E_NORMAL_RESTART == dwExitCode || wcsncmp(argv[i], MY_STRING(L"-env:")) == 0) {
                p = desktop_win32::commandLineAppend(p, MY_STRING(L"\" \""));
                p = desktop_win32::commandLineAppend(p, argv[i]);
            }
        }

        p = desktop_win32::commandLineAppend(
            p, MY_STRING(L"\" \"-env:OOO_CWD="));
        if (cwdLen == 0) {
            p = desktop_win32::commandLineAppend(p, MY_STRING(L"0"));
        } else {
            p = desktop_win32::commandLineAppend(p, MY_STRING(L"2"));
            p = desktop_win32::commandLineAppendEncoded(p, cwd);
        }
        desktop_win32::commandLineAppend(p, MY_STRING(L"\""));
        bFirst = false;

        TCHAR   szParentProcessId[64]; // This is more than large enough for a 128 bit decimal value
        BOOL    bHeadlessMode( FALSE );

        {
            // Check command line arguments for "--headless" parameter. We only
            // set the environment variable "ATTACHED_PARENT_PROCESSID" for the headless
            // mode as self-destruction of the soffice.bin process can lead to
            // certain side-effects (log-off can result in data-loss, ".lock" is not deleted.
            // See 138244 for more information.
            int     argc;
            LPTSTR  *argv = GetCommandArgs( &argc );

            if ( argc > 1 )
            {
                int n;

                for ( n = 1; n < argc; n++ )
                {
                    if ( 0 == _tcsnicmp( argv[n], _T("-headless"), 9 ) ||
                         0 == _tcsnicmp( argv[n], _T("--headless"), 9 ) )
                    {
                        bHeadlessMode = TRUE;
                    }
                }
            }
        }

        if ( _ltot( (long)GetCurrentProcessId(),szParentProcessId, 10 ) && bHeadlessMode )
            SetEnvironmentVariable( TEXT("ATTACHED_PARENT_PROCESSID"), szParentProcessId );

        PROCESS_INFORMATION aProcessInfo;

        fSuccess = CreateProcess(
            szTargetFileName,
            lpCommandLine,
            NULL,
            NULL,
            TRUE,
            0,
            NULL,
            szIniDirectory,
            &aStartupInfo,
            &aProcessInfo );

        if ( fSuccess )
        {
            DWORD   dwWaitResult;

            do
            {
                // On Windows XP it seems as the desktop calls WaitForInputIdle after "OpenWidth" so we have to do so
                // as if we where processing any messages

                dwWaitResult = MsgWaitForMultipleObjects( 1, &aProcessInfo.hProcess, FALSE, INFINITE, QS_ALLEVENTS );

                if (  WAIT_OBJECT_0 + 1 == dwWaitResult )
                {
                    MSG msg;

                    PeekMessage( &msg, NULL, 0, 0, PM_REMOVE );
                }
            } while ( WAIT_OBJECT_0 + 1 == dwWaitResult );

            dwExitCode = 0;
            GetExitCodeProcess( aProcessInfo.hProcess, &dwExitCode );

            CloseHandle( aProcessInfo.hProcess );
            CloseHandle( aProcessInfo.hThread );
        }
    } while ( false );
    delete[] lpCommandLine;

    return fSuccess ? dwExitCode : -1;
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<|endoftext|>


<commit_before>/**
 * @file llnamelistctrl.cpp
 * @brief A list of names, automatically refreshed from name cache.
 *
 * $LicenseInfo:firstyear=2003&license=viewergpl$
 *
 * Copyright (c) 2003-2009, Linden Research, Inc.
 *
 * Second Life Viewer Source Code
 * The source code in this file ("Source Code") is provided by Linden Lab
 * to you under the terms of the GNU General Public License, version 2.0
 * ("GPL"), unless you have obtained a separate licensing agreement
 * ("Other License"), formally executed by you and Linden Lab.  Terms of
 * the GPL can be found in doc/GPL-license.txt in this distribution, or
 * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
 *
 * There are special exceptions to the terms and conditions of the GPL as
 * it is applied to this Source Code. View the full text of the exception
 * in the file doc/FLOSS-exception.txt in this software distribution, or
 * online at
 * http://secondlifegrid.net/programs/open_source/licensing/flossexception
 *
 * By copying, modifying or distributing this software, you acknowledge
 * that you have read and understood your obligations described above,
 * and agree to abide by those obligations.
 *
 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
 * COMPLETENESS OR PERFORMANCE.
 * $/LicenseInfo$
 */

#include "llviewerprecompiledheaders.h"

#include "llnamelistctrl.h"

#include <boost/tokenizer.hpp>

#include "llcachename.h"
#include "llfloaterreg.h"
#include "llinventory.h"
#include "llscrolllistitem.h"
#include "llscrolllistcell.h"
#include "llscrolllistcolumn.h"
#include "llsdparam.h"
#include "lltooltip.h"

static LLDefaultChildRegistry::Register<LLNameListCtrl> r("name_list");

void LLNameListCtrl::NameTypeNames::declareValues()
{
	declare("INDIVIDUAL", LLNameListCtrl::INDIVIDUAL);
	declare("GROUP", LLNameListCtrl::GROUP);
	declare("SPECIAL", LLNameListCtrl::SPECIAL);
}

LLNameListCtrl::Params::Params()
:	name_column(""),
	allow_calling_card_drop("allow_calling_card_drop", false)
{
	name = "name_list";
}

LLNameListCtrl::LLNameListCtrl(const LLNameListCtrl::Params& p)
:	LLScrollListCtrl(p),
	mNameColumnIndex(p.name_column.column_index),
	mNameColumn(p.name_column.column_name),
	mAllowCallingCardDrop(p.allow_calling_card_drop)
{}

// public
void LLNameListCtrl::addNameItem(const LLUUID& agent_id, EAddPosition pos,
								 BOOL enabled, std::string& suffix)
{
	//llinfos << "LLNameListCtrl::addNameItem " << agent_id << llendl;

	NameItem item;
	item.value = agent_id;
	item.enabled = enabled;
	item.target = INDIVIDUAL;

	addNameItemRow(item, pos);
}

// virtual, public
BOOL LLNameListCtrl::handleDragAndDrop(
		S32 x, S32 y, MASK mask,
		BOOL drop,
		EDragAndDropType cargo_type, void *cargo_data,
		EAcceptance *accept,
		std::string& tooltip_msg)
{
	if (!mAllowCallingCardDrop)
	{
		return FALSE;
	}

	BOOL handled = FALSE;

	if (cargo_type == DAD_CALLINGCARD)
	{
		if (drop)
		{
			LLInventoryItem* item = (LLInventoryItem *)cargo_data;
			addNameItem(item->getCreatorUUID());
		}

		*accept = ACCEPT_YES_MULTI;
	}
	else
	{
		*accept = ACCEPT_NO;
		if (tooltip_msg.empty())
		{
			if (!getToolTip().empty())
			{
				tooltip_msg = getToolTip();
			}
			else
			{
				// backwards compatable English tooltip (should be overridden in xml)
				tooltip_msg.assign("Drag a calling card here\nto add a resident.");
			}
		}
	}

	handled = TRUE;
	lldebugst(LLERR_USER_INPUT) << "dragAndDrop handled by LLNameListCtrl " << getName() << llendl;

	return handled;
}

void LLNameListCtrl::showInspector(const LLUUID& avatar_id, bool is_group)
{
	if (is_group)
		LLFloaterReg::showInstance("inspect_group", LLSD().with("group_id", avatar_id));
	else
		LLFloaterReg::showInstance("inspect_avatar", LLSD().with("avatar_id", avatar_id));
}

//virtual
BOOL LLNameListCtrl::handleToolTip(S32 x, S32 y, MASK mask)
{
	BOOL handled = FALSE;
	S32 column_index = getColumnIndexFromOffset(x);
	LLScrollListItem* hit_item = hitItem(x, y);
	if (hit_item
		&& column_index == mNameColumnIndex)
	{
		// ...this is the column with the avatar name
		LLUUID avatar_id = hit_item->getUUID();
		if (avatar_id.notNull())
		{
			// ...valid avatar id
			LLScrollListCell* hit_cell = hit_item->getColumn(column_index);
			if (hit_cell)
			{
				S32 row_index = getItemIndex(hit_item);
				LLRect cell_rect = getCellRect(row_index, column_index);
				// Convert rect local to screen coordinates
				LLRect sticky_rect;
				localRectToScreen(cell_rect, &sticky_rect);

				// Spawn at right side of cell
				LLCoordGL pos( sticky_rect.mRight - 16, sticky_rect.mTop + (sticky_rect.getHeight()-16)/2 );
				LLPointer<LLUIImage> icon = LLUI::getUIImage("Info_Small");

				// Should we show a group or an avatar inspector?
				bool is_group = hit_item->getValue()["is_group"].asBoolean();

				LLToolTip::Params params;
				params.background_visible( false );
				params.click_callback( boost::bind(&LLNameListCtrl::showInspector, this, avatar_id, is_group) );
				params.delay_time(0.0f);		// spawn instantly on hover
				params.image( icon );
				params.message("");
				params.padding(0);
				params.pos(pos);
				params.sticky_rect(sticky_rect);

				LLToolTipMgr::getInstance()->show(params);
				handled = TRUE;
			}
		}
	}
	if (!handled)
	{
		handled = LLScrollListCtrl::handleToolTip(x, y, mask);
	}
	return handled;
}

// public
void LLNameListCtrl::addGroupNameItem(const LLUUID& group_id, EAddPosition pos,
									  BOOL enabled)
{
	NameItem item;
	item.value = group_id;
	item.enabled = enabled;
	item.target = GROUP;

	addNameItemRow(item, pos);
}

// public
void LLNameListCtrl::addGroupNameItem(LLNameListCtrl::NameItem& item, EAddPosition pos)
{
	item.target = GROUP;
	addNameItemRow(item, pos);
}

void LLNameListCtrl::addNameItem(LLNameListCtrl::NameItem& item, EAddPosition pos)
{
	item.target = INDIVIDUAL;
	addNameItemRow(item, pos);
}

LLScrollListItem* LLNameListCtrl::addElement(const LLSD& element, EAddPosition pos, void* userdata)
{
	LLNameListCtrl::NameItem item_params;
	LLParamSDParser::instance().readSD(element, item_params);
	item_params.userdata = userdata;
	return addNameItemRow(item_params, pos);
}

LLScrollListItem* LLNameListCtrl::addNameItemRow(
	const LLNameListCtrl::NameItem& name_item,
	EAddPosition pos,
	std::string& suffix)
{
	LLUUID id = name_item.value().asUUID();
	LLNameListItem* item = NULL;

	// Store item type so that we can invoke the proper inspector.
	// *TODO Vadim: Is there a more proper way of storing additional item data?
	{
		LLNameListCtrl::NameItem item_p(name_item);
		item_p.value = LLSD().with("uuid", id).with("is_group", name_item.target() == GROUP);
		item = new LLNameListItem(item_p);
		LLScrollListCtrl::addRow(item, item_p, pos);
	}

	if (!item) return NULL;

	// use supplied name by default
	std::string fullname = name_item.name;
	switch(name_item.target)
	{
	case GROUP:
		gCacheName->getGroupName(id, fullname);
		// fullname will be "nobody" if group not found
		break;
	case SPECIAL:
		// just use supplied name
		break;
	case INDIVIDUAL:
		{
			std::string name;
			if (gCacheName->getFullName(id, name))
			{
				fullname = name;
			}
			break;
		}
	default:
		break;
	}

	// Append optional suffix.
	if (!suffix.empty())
	{
		fullname.append(suffix);
	}

	LLScrollListCell* cell = item->getColumn(mNameColumnIndex);
	if (cell)
	{
		cell->setValue(fullname);
	}

	dirtyColumns();

	// this column is resizable
	LLScrollListColumn* columnp = getColumn(mNameColumnIndex);
	if (columnp && columnp->mHeader)
	{
		columnp->mHeader->setHasResizableElement(TRUE);
	}

	return item;
}

// public
void LLNameListCtrl::removeNameItem(const LLUUID& agent_id)
{
	// Find the item specified with agent_id.
	S32 idx = -1;
	for (item_list::iterator it = getItemList().begin(); it != getItemList().end(); it++)
	{
		LLScrollListItem* item = *it;
		if (item->getUUID() == agent_id)
		{
			idx = getItemIndex(item);
			break;
		}
	}

	// Remove it.
	if (idx >= 0)
	{
		selectNthItem(idx); // not sure whether this is needed, taken from previous implementation
		deleteSingleItem(idx);
	}
}

// public
void LLNameListCtrl::refresh(const LLUUID& id, const std::string& first,
							 const std::string& last, BOOL is_group)
{
	//llinfos << "LLNameListCtrl::refresh " << id << " '" << first << " "
	//	<< last << "'" << llendl;

	std::string fullname;
	if (!is_group)
	{
		fullname = first + " " + last;
	}
	else
	{
		fullname = first;
	}

	// TODO: scan items for that ID, fix if necessary
	item_list::iterator iter;
	for (iter = getItemList().begin(); iter != getItemList().end(); iter++)
	{
		LLScrollListItem* item = *iter;
		if (item->getUUID() == id)
		{
			LLScrollListCell* cell = (LLScrollListCell*)item->getColumn(0);
			cell = item->getColumn(mNameColumnIndex);
			if (cell)
			{
				cell->setValue(fullname);
			}
		}
	}

	dirtyColumns();
}

// static
void LLNameListCtrl::refreshAll(const LLUUID& id, const std::string& first,
								const std::string& last, BOOL is_group)
{
	LLInstanceTracker<LLNameListCtrl>::instance_iter it;
	for (it = beginInstances(); it != endInstances(); ++it)
	{
		LLNameListCtrl& ctrl = *it;
		ctrl.refresh(id, first, last, is_group);
	}
}

void LLNameListCtrl::updateColumns()
{
	LLScrollListCtrl::updateColumns();

	if (!mNameColumn.empty())
	{
		LLScrollListColumn* name_column = getColumn(mNameColumn);
		if (name_column)
		{
			mNameColumnIndex = name_column->mIndex;
		}
	}
}
<commit_msg>fix for normal EXT-3807 ABOUT LAND/OBJECTS: (i) icon is badly positioned<commit_after>/**
 * @file llnamelistctrl.cpp
 * @brief A list of names, automatically refreshed from name cache.
 *
 * $LicenseInfo:firstyear=2003&license=viewergpl$
 *
 * Copyright (c) 2003-2009, Linden Research, Inc.
 *
 * Second Life Viewer Source Code
 * The source code in this file ("Source Code") is provided by Linden Lab
 * to you under the terms of the GNU General Public License, version 2.0
 * ("GPL"), unless you have obtained a separate licensing agreement
 * ("Other License"), formally executed by you and Linden Lab.  Terms of
 * the GPL can be found in doc/GPL-license.txt in this distribution, or
 * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
 *
 * There are special exceptions to the terms and conditions of the GPL as
 * it is applied to this Source Code. View the full text of the exception
 * in the file doc/FLOSS-exception.txt in this software distribution, or
 * online at
 * http://secondlifegrid.net/programs/open_source/licensing/flossexception
 *
 * By copying, modifying or distributing this software, you acknowledge
 * that you have read and understood your obligations described above,
 * and agree to abide by those obligations.
 *
 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
 * COMPLETENESS OR PERFORMANCE.
 * $/LicenseInfo$
 */

#include "llviewerprecompiledheaders.h"

#include "llnamelistctrl.h"

#include <boost/tokenizer.hpp>

#include "llcachename.h"
#include "llfloaterreg.h"
#include "llinventory.h"
#include "llscrolllistitem.h"
#include "llscrolllistcell.h"
#include "llscrolllistcolumn.h"
#include "llsdparam.h"
#include "lltooltip.h"

static LLDefaultChildRegistry::Register<LLNameListCtrl> r("name_list");

void LLNameListCtrl::NameTypeNames::declareValues()
{
	declare("INDIVIDUAL", LLNameListCtrl::INDIVIDUAL);
	declare("GROUP", LLNameListCtrl::GROUP);
	declare("SPECIAL", LLNameListCtrl::SPECIAL);
}

LLNameListCtrl::Params::Params()
:	name_column(""),
	allow_calling_card_drop("allow_calling_card_drop", false)
{
	name = "name_list";
}

LLNameListCtrl::LLNameListCtrl(const LLNameListCtrl::Params& p)
:	LLScrollListCtrl(p),
	mNameColumnIndex(p.name_column.column_index),
	mNameColumn(p.name_column.column_name),
	mAllowCallingCardDrop(p.allow_calling_card_drop)
{}

// public
void LLNameListCtrl::addNameItem(const LLUUID& agent_id, EAddPosition pos,
								 BOOL enabled, std::string& suffix)
{
	//llinfos << "LLNameListCtrl::addNameItem " << agent_id << llendl;

	NameItem item;
	item.value = agent_id;
	item.enabled = enabled;
	item.target = INDIVIDUAL;

	addNameItemRow(item, pos);
}

// virtual, public
BOOL LLNameListCtrl::handleDragAndDrop(
		S32 x, S32 y, MASK mask,
		BOOL drop,
		EDragAndDropType cargo_type, void *cargo_data,
		EAcceptance *accept,
		std::string& tooltip_msg)
{
	if (!mAllowCallingCardDrop)
	{
		return FALSE;
	}

	BOOL handled = FALSE;

	if (cargo_type == DAD_CALLINGCARD)
	{
		if (drop)
		{
			LLInventoryItem* item = (LLInventoryItem *)cargo_data;
			addNameItem(item->getCreatorUUID());
		}

		*accept = ACCEPT_YES_MULTI;
	}
	else
	{
		*accept = ACCEPT_NO;
		if (tooltip_msg.empty())
		{
			if (!getToolTip().empty())
			{
				tooltip_msg = getToolTip();
			}
			else
			{
				// backwards compatable English tooltip (should be overridden in xml)
				tooltip_msg.assign("Drag a calling card here\nto add a resident.");
			}
		}
	}

	handled = TRUE;
	lldebugst(LLERR_USER_INPUT) << "dragAndDrop handled by LLNameListCtrl " << getName() << llendl;

	return handled;
}

void LLNameListCtrl::showInspector(const LLUUID& avatar_id, bool is_group)
{
	if (is_group)
		LLFloaterReg::showInstance("inspect_group", LLSD().with("group_id", avatar_id));
	else
		LLFloaterReg::showInstance("inspect_avatar", LLSD().with("avatar_id", avatar_id));
}

//virtual
BOOL LLNameListCtrl::handleToolTip(S32 x, S32 y, MASK mask)
{
	BOOL handled = FALSE;
	S32 column_index = getColumnIndexFromOffset(x);
	LLScrollListItem* hit_item = hitItem(x, y);
	if (hit_item
		&& column_index == mNameColumnIndex)
	{
		// ...this is the column with the avatar name
		LLUUID avatar_id = hit_item->getUUID();
		if (avatar_id.notNull())
		{
			// ...valid avatar id

			LLScrollListCell* hit_cell = hit_item->getColumn(column_index);
			if (hit_cell)
			{
				S32 row_index = getItemIndex(hit_item);
				LLRect cell_rect = getCellRect(row_index, column_index);
				// Convert rect local to screen coordinates
				LLRect sticky_rect;
				localRectToScreen(cell_rect, &sticky_rect);

				// Spawn at right side of cell
				LLPointer<LLUIImage> icon = LLUI::getUIImage("Info_Small");
				LLCoordGL pos( sticky_rect.mRight - 16, sticky_rect.mTop - (sticky_rect.getHeight() - icon->getHeight())/2 );

				// Should we show a group or an avatar inspector?
				bool is_group = hit_item->getValue()["is_group"].asBoolean();

				LLToolTip::Params params;
				params.background_visible( false );
				params.click_callback( boost::bind(&LLNameListCtrl::showInspector, this, avatar_id, is_group) );
				params.delay_time(0.0f);		// spawn instantly on hover
				params.image( icon );
				params.message("");
				params.padding(0);
				params.pos(pos);
				params.sticky_rect(sticky_rect);

				LLToolTipMgr::getInstance()->show(params);
				handled = TRUE;
			}
		}
	}
	if (!handled)
	{
		handled = LLScrollListCtrl::handleToolTip(x, y, mask);
	}
	return handled;
}

// public
void LLNameListCtrl::addGroupNameItem(const LLUUID& group_id, EAddPosition pos,
									  BOOL enabled)
{
	NameItem item;
	item.value = group_id;
	item.enabled = enabled;
	item.target = GROUP;

	addNameItemRow(item, pos);
}

// public
void LLNameListCtrl::addGroupNameItem(LLNameListCtrl::NameItem& item, EAddPosition pos)
{
	item.target = GROUP;
	addNameItemRow(item, pos);
}

void LLNameListCtrl::addNameItem(LLNameListCtrl::NameItem& item, EAddPosition pos)
{
	item.target = INDIVIDUAL;
	addNameItemRow(item, pos);
}

LLScrollListItem* LLNameListCtrl::addElement(const LLSD& element, EAddPosition pos, void* userdata)
{
	LLNameListCtrl::NameItem item_params;
	LLParamSDParser::instance().readSD(element, item_params);
	item_params.userdata = userdata;
	return addNameItemRow(item_params, pos);
}

LLScrollListItem* LLNameListCtrl::addNameItemRow(
	const LLNameListCtrl::NameItem& name_item,
	EAddPosition pos,
	std::string& suffix)
{
	LLUUID id = name_item.value().asUUID();
	LLNameListItem* item = NULL;

	// Store item type so that we can invoke the proper inspector.
	// *TODO Vadim: Is there a more proper way of storing additional item data?
	{
		LLNameListCtrl::NameItem item_p(name_item);
		item_p.value = LLSD().with("uuid", id).with("is_group", name_item.target() == GROUP);
		item = new LLNameListItem(item_p);
		LLScrollListCtrl::addRow(item, item_p, pos);
	}

	if (!item) return NULL;

	// use supplied name by default
	std::string fullname = name_item.name;
	switch(name_item.target)
	{
	case GROUP:
		gCacheName->getGroupName(id, fullname);
		// fullname will be "nobody" if group not found
		break;
	case SPECIAL:
		// just use supplied name
		break;
	case INDIVIDUAL:
		{
			std::string name;
			if (gCacheName->getFullName(id, name))
			{
				fullname = name;
			}
			break;
		}
	default:
		break;
	}

	// Append optional suffix.
	if (!suffix.empty())
	{
		fullname.append(suffix);
	}

	LLScrollListCell* cell = item->getColumn(mNameColumnIndex);
	if (cell)
	{
		cell->setValue(fullname);
	}

	dirtyColumns();

	// this column is resizable
	LLScrollListColumn* columnp = getColumn(mNameColumnIndex);
	if (columnp && columnp->mHeader)
	{
		columnp->mHeader->setHasResizableElement(TRUE);
	}

	return item;
}

// public
void LLNameListCtrl::removeNameItem(const LLUUID& agent_id)
{
	// Find the item specified with agent_id.
	S32 idx = -1;
	for (item_list::iterator it = getItemList().begin(); it != getItemList().end(); it++)
	{
		LLScrollListItem* item = *it;
		if (item->getUUID() == agent_id)
		{
			idx = getItemIndex(item);
			break;
		}
	}

	// Remove it.
	if (idx >= 0)
	{
		selectNthItem(idx); // not sure whether this is needed, taken from previous implementation
		deleteSingleItem(idx);
	}
}

// public
void LLNameListCtrl::refresh(const LLUUID& id, const std::string& first,
							 const std::string& last, BOOL is_group)
{
	//llinfos << "LLNameListCtrl::refresh " << id << " '" << first << " "
	//	<< last << "'" << llendl;

	std::string fullname;
	if (!is_group)
	{
		fullname = first + " " + last;
	}
	else
	{
		fullname = first;
	}

	// TODO: scan items for that ID, fix if necessary
	item_list::iterator iter;
	for (iter = getItemList().begin(); iter != getItemList().end(); iter++)
	{
		LLScrollListItem* item = *iter;
		if (item->getUUID() == id)
		{
			LLScrollListCell* cell = (LLScrollListCell*)item->getColumn(0);
			cell = item->getColumn(mNameColumnIndex);
			if (cell)
			{
				cell->setValue(fullname);
			}
		}
	}

	dirtyColumns();
}

// static
void LLNameListCtrl::refreshAll(const LLUUID& id, const std::string& first,
								const std::string& last, BOOL is_group)
{
	LLInstanceTracker<LLNameListCtrl>::instance_iter it;
	for (it = beginInstances(); it != endInstances(); ++it)
	{
		LLNameListCtrl& ctrl = *it;
		ctrl.refresh(id, first, last, is_group);
	}
}

void LLNameListCtrl::updateColumns()
{
	LLScrollListCtrl::updateColumns();

	if (!mNameColumn.empty())
	{
		LLScrollListColumn* name_column = getColumn(mNameColumn);
		if (name_column)
		{
			mNameColumnIndex = name_column->mIndex;
		}
	}
}
<|endoftext|>


<commit_before>/* Copyright 2015 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

#include "tensorflow/core/framework/op_gen_lib.h"

#include <vector>
#include "tensorflow/core/lib/core/errors.h"
#include "tensorflow/core/lib/strings/str_util.h"
#include "tensorflow/core/lib/strings/strcat.h"

namespace tensorflow {

string WordWrap(StringPiece prefix, StringPiece str, int width) {
  const string indent_next_line = "\n" + Spaces(prefix.size());
  width -= prefix.size();
  string result;
  strings::StrAppend(&result, prefix);

  while (!str.empty()) {
    if (static_cast<int>(str.size()) <= width) {
      // Remaining text fits on one line.
      strings::StrAppend(&result, str);
      break;
    }
    auto space = str.rfind(' ', width);
    if (space == StringPiece::npos) {
      // Rather make a too-long line and break at a space.
      space = str.find(' ');
      if (space == StringPiece::npos) {
        strings::StrAppend(&result, str);
        break;
      }
    }
    // Breaking at character at position <space>.
    StringPiece to_append = str.substr(0, space);
    str.remove_prefix(space + 1);
    // Remove spaces at break.
    while (to_append.ends_with(" ")) {
      to_append.remove_suffix(1);
    }
    while (str.Consume(" ")) {
    }

    // Go on to the next line.
    strings::StrAppend(&result, to_append);
    if (!str.empty()) strings::StrAppend(&result, indent_next_line);
  }

  return result;
}

bool ConsumeEquals(StringPiece* description) {
  if (description->Consume("=")) {
    while (description->Consume(" ")) {  // Also remove spaces after "=".
    }
    return true;
  }
  return false;
}

Status OpGenOverrideMap::LoadFileList(Env* env, const string& filenames) {
  std::vector<string> v = str_util::Split(filenames, ",");
  for (const string& f : v) {
    TF_RETURN_IF_ERROR(LoadFile(env, f));
  }
  return Status::OK();
}

Status OpGenOverrideMap::LoadFile(Env* env, const string& filename) {
  if (filename.empty()) return Status::OK();
  string contents;
  TF_RETURN_IF_ERROR(ReadFileToString(env, filename, &contents));
  OpGenOverrides all;
  protobuf::TextFormat::ParseFromString(contents, &all);
  for (const auto& one : all.op()) {
    map_[one.name()] = one;
  }
  return Status::OK();
}

static void StringReplace(const string& from, const string& to, string* s) {
  std::vector<string> rest = str_util::Split(*s, from);
  *s = str_util::Join(rest, to.c_str());
}

static void RenameInDocs(const string& from, const string& to, OpDef* op_def) {
  const string from_quoted = strings::StrCat("`", from, "`");
  const string to_quoted = strings::StrCat("`", to, "`");
  for (int i = 0; i < op_def->input_arg_size(); ++i) {
    if (!op_def->input_arg(i).description().empty()) {
      StringReplace(from_quoted, to_quoted,
                    op_def->mutable_input_arg(i)->mutable_description());
    }
  }
  for (int i = 0; i < op_def->output_arg_size(); ++i) {
    if (!op_def->output_arg(i).description().empty()) {
      StringReplace(from_quoted, to_quoted,
                    op_def->mutable_output_arg(i)->mutable_description());
    }
  }
  for (int i = 0; i < op_def->attr_size(); ++i) {
    if (!op_def->attr(i).description().empty()) {
      StringReplace(from_quoted, to_quoted,
                    op_def->mutable_attr(i)->mutable_description());
    }
  }
  if (!op_def->summary().empty()) {
    StringReplace(from_quoted, to_quoted, op_def->mutable_summary());
  }
  if (!op_def->description().empty()) {
    StringReplace(from_quoted, to_quoted, op_def->mutable_summary());
  }
}

const OpGenOverride* OpGenOverrideMap::ApplyOverride(OpDef* op_def) const {
  // Look up
  const auto iter = map_.find(op_def->name());
  if (iter == map_.end()) return nullptr;
  const OpGenOverride& proto = iter->second;

  // Apply overrides from `proto`.
  if (!proto.rename_to().empty()) {
    op_def->set_name(proto.rename_to());
    RenameInDocs(proto.name(), proto.rename_to(), op_def);
  }
  for (const auto& attr_default : proto.attr_default()) {
    bool found = false;
    for (int i = 0; i < op_def->attr_size(); ++i) {
      if (op_def->attr(i).name() == attr_default.name()) {
        *op_def->mutable_attr(i)->mutable_default_value() =
            attr_default.value();
        found = true;
        break;
      }
    }
    if (!found) {
      LOG(WARNING) << proto.name() << " can't find attr " << attr_default.name()
                   << " to override default";
    }
  }
  for (const auto& attr_rename : proto.attr_rename()) {
    bool found = false;
    for (int i = 0; i < op_def->attr_size(); ++i) {
      if (op_def->attr(i).name() == attr_rename.from()) {
        *op_def->mutable_attr(i)->mutable_name() = attr_rename.to();
        found = true;
        break;
      }
    }
    if (found) {
      RenameInDocs(attr_rename.from(), attr_rename.to(), op_def);
    } else {
      LOG(WARNING) << proto.name() << " can't find attr " << attr_rename.from()
                   << " to rename";
    }
  }
  for (const auto& input_rename : proto.input_rename()) {
    bool found = false;
    for (int i = 0; i < op_def->input_arg_size(); ++i) {
      if (op_def->input_arg(i).name() == input_rename.from()) {
        *op_def->mutable_input_arg(i)->mutable_name() = input_rename.to();
        found = true;
        break;
      }
    }
    if (found) {
      RenameInDocs(input_rename.from(), input_rename.to(), op_def);
    } else {
      LOG(WARNING) << proto.name() << " can't find input "
                   << input_rename.from() << " to rename";
    }
  }
  for (const auto& output_rename : proto.output_rename()) {
    bool found = false;
    for (int i = 0; i < op_def->output_arg_size(); ++i) {
      if (op_def->output_arg(i).name() == output_rename.from()) {
        *op_def->mutable_output_arg(i)->mutable_name() = output_rename.to();
        found = true;
        break;
      }
    }
    if (found) {
      RenameInDocs(output_rename.from(), output_rename.to(), op_def);
    } else {
      LOG(WARNING) << proto.name() << " can't find output "
                   << output_rename.from() << " to rename";
    }
  }

  return &proto;
}

}  // namespace tensorflow
<commit_msg>Fix bug with doc updates for ops with renames in the C++ API. Change: 146301808<commit_after>/* Copyright 2015 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

#include "tensorflow/core/framework/op_gen_lib.h"

#include <vector>
#include "tensorflow/core/lib/core/errors.h"
#include "tensorflow/core/lib/strings/str_util.h"
#include "tensorflow/core/lib/strings/strcat.h"

namespace tensorflow {

string WordWrap(StringPiece prefix, StringPiece str, int width) {
  const string indent_next_line = "\n" + Spaces(prefix.size());
  width -= prefix.size();
  string result;
  strings::StrAppend(&result, prefix);

  while (!str.empty()) {
    if (static_cast<int>(str.size()) <= width) {
      // Remaining text fits on one line.
      strings::StrAppend(&result, str);
      break;
    }
    auto space = str.rfind(' ', width);
    if (space == StringPiece::npos) {
      // Rather make a too-long line and break at a space.
      space = str.find(' ');
      if (space == StringPiece::npos) {
        strings::StrAppend(&result, str);
        break;
      }
    }
    // Breaking at character at position <space>.
    StringPiece to_append = str.substr(0, space);
    str.remove_prefix(space + 1);
    // Remove spaces at break.
    while (to_append.ends_with(" ")) {
      to_append.remove_suffix(1);
    }
    while (str.Consume(" ")) {
    }

    // Go on to the next line.
    strings::StrAppend(&result, to_append);
    if (!str.empty()) strings::StrAppend(&result, indent_next_line);
  }

  return result;
}

bool ConsumeEquals(StringPiece* description) {
  if (description->Consume("=")) {
    while (description->Consume(" ")) {  // Also remove spaces after "=".
    }
    return true;
  }
  return false;
}

Status OpGenOverrideMap::LoadFileList(Env* env, const string& filenames) {
  std::vector<string> v = str_util::Split(filenames, ",");
  for (const string& f : v) {
    TF_RETURN_IF_ERROR(LoadFile(env, f));
  }
  return Status::OK();
}

Status OpGenOverrideMap::LoadFile(Env* env, const string& filename) {
  if (filename.empty()) return Status::OK();
  string contents;
  TF_RETURN_IF_ERROR(ReadFileToString(env, filename, &contents));
  OpGenOverrides all;
  protobuf::TextFormat::ParseFromString(contents, &all);
  for (const auto& one : all.op()) {
    map_[one.name()] = one;
  }
  return Status::OK();
}

static void StringReplace(const string& from, const string& to, string* s) {
  // Split *s into pieces delimited by `from`.
  std::vector<string> split;
  string::size_type pos = 0;
  while (pos < s->size()) {
    auto found = s->find(from, pos);
    if (found == string::npos) {
      split.push_back(s->substr(pos));
      break;
    } else {
      split.push_back(s->substr(pos, found - pos));
      pos = found + from.size();
    }
  }
  // Join the pieces back together with a new delimiter.
  *s = str_util::Join(split, to.c_str());
}

static void RenameInDocs(const string& from, const string& to, OpDef* op_def) {
  const string from_quoted = strings::StrCat("`", from, "`");
  const string to_quoted = strings::StrCat("`", to, "`");
  for (int i = 0; i < op_def->input_arg_size(); ++i) {
    if (!op_def->input_arg(i).description().empty()) {
      StringReplace(from_quoted, to_quoted,
                    op_def->mutable_input_arg(i)->mutable_description());
    }
  }
  for (int i = 0; i < op_def->output_arg_size(); ++i) {
    if (!op_def->output_arg(i).description().empty()) {
      StringReplace(from_quoted, to_quoted,
                    op_def->mutable_output_arg(i)->mutable_description());
    }
  }
  for (int i = 0; i < op_def->attr_size(); ++i) {
    if (!op_def->attr(i).description().empty()) {
      StringReplace(from_quoted, to_quoted,
                    op_def->mutable_attr(i)->mutable_description());
    }
  }
  if (!op_def->summary().empty()) {
    StringReplace(from_quoted, to_quoted, op_def->mutable_summary());
  }
  if (!op_def->description().empty()) {
    StringReplace(from_quoted, to_quoted, op_def->mutable_description());
  }
}

const OpGenOverride* OpGenOverrideMap::ApplyOverride(OpDef* op_def) const {
  // Look up
  const auto iter = map_.find(op_def->name());
  if (iter == map_.end()) return nullptr;
  const OpGenOverride& proto = iter->second;

  // Apply overrides from `proto`.
  if (!proto.rename_to().empty()) {
    op_def->set_name(proto.rename_to());
    RenameInDocs(proto.name(), proto.rename_to(), op_def);
  }
  for (const auto& attr_default : proto.attr_default()) {
    bool found = false;
    for (int i = 0; i < op_def->attr_size(); ++i) {
      if (op_def->attr(i).name() == attr_default.name()) {
        *op_def->mutable_attr(i)->mutable_default_value() =
            attr_default.value();
        found = true;
        break;
      }
    }
    if (!found) {
      LOG(WARNING) << proto.name() << " can't find attr " << attr_default.name()
                   << " to override default";
    }
  }
  for (const auto& attr_rename : proto.attr_rename()) {
    bool found = false;
    for (int i = 0; i < op_def->attr_size(); ++i) {
      if (op_def->attr(i).name() == attr_rename.from()) {
        *op_def->mutable_attr(i)->mutable_name() = attr_rename.to();
        found = true;
        break;
      }
    }
    if (found) {
      RenameInDocs(attr_rename.from(), attr_rename.to(), op_def);
    } else {
      LOG(WARNING) << proto.name() << " can't find attr " << attr_rename.from()
                   << " to rename";
    }
  }
  for (const auto& input_rename : proto.input_rename()) {
    bool found = false;
    for (int i = 0; i < op_def->input_arg_size(); ++i) {
      if (op_def->input_arg(i).name() == input_rename.from()) {
        *op_def->mutable_input_arg(i)->mutable_name() = input_rename.to();
        found = true;
        break;
      }
    }
    if (found) {
      RenameInDocs(input_rename.from(), input_rename.to(), op_def);
    } else {
      LOG(WARNING) << proto.name() << " can't find input "
                   << input_rename.from() << " to rename";
    }
  }
  for (const auto& output_rename : proto.output_rename()) {
    bool found = false;
    for (int i = 0; i < op_def->output_arg_size(); ++i) {
      if (op_def->output_arg(i).name() == output_rename.from()) {
        *op_def->mutable_output_arg(i)->mutable_name() = output_rename.to();
        found = true;
        break;
      }
    }
    if (found) {
      RenameInDocs(output_rename.from(), output_rename.to(), op_def);
    } else {
      LOG(WARNING) << proto.name() << " can't find output "
                   << output_rename.from() << " to rename";
    }
  }

  return &proto;
}

}  // namespace tensorflow
<|endoftext|>


<commit_before>/* Copyright 2015 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <fnmatch.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>

#ifdef __FreeBSD__
#include <pthread_np.h>
#endif

#include <thread>
#include <vector>

#include "tensorflow/core/platform/default/posix_file_system.h"
#include "tensorflow/core/platform/env.h"
#include "tensorflow/core/platform/load_library.h"
#include "tensorflow/core/platform/logging.h"
#include "tensorflow/core/platform/mutex.h"
#include "tensorflow/core/platform/ram_file_system.h"
#include "tensorflow/core/protobuf/error_codes.pb.h"

namespace tensorflow {

namespace {

mutex name_mutex(tensorflow::LINKER_INITIALIZED);

std::map<std::thread::id, string>& GetThreadNameRegistry()
    TF_EXCLUSIVE_LOCKS_REQUIRED(name_mutex) {
  static auto* thread_name_registry = new std::map<std::thread::id, string>();
  return *thread_name_registry;
}

class StdThread : public Thread {
 public:
  // thread_options is ignored.
  StdThread(const ThreadOptions& thread_options, const string& name,
            std::function<void()> fn)
      : thread_(fn) {
    mutex_lock l(name_mutex);
    GetThreadNameRegistry().emplace(thread_.get_id(), name);
  }

  ~StdThread() override {
    std::thread::id thread_id = thread_.get_id();
    thread_.join();
    mutex_lock l(name_mutex);
    GetThreadNameRegistry().erase(thread_id);
  }

 private:
  std::thread thread_;
};

class PosixEnv : public Env {
 public:
  PosixEnv() {}

  ~PosixEnv() override { LOG(FATAL) << "Env::Default() must not be destroyed"; }

  bool MatchPath(const string& path, const string& pattern) override {
    return fnmatch(pattern.c_str(), path.c_str(), FNM_PATHNAME) == 0;
  }

  void SleepForMicroseconds(int64 micros) override {
    while (micros > 0) {
      timespec sleep_time;
      sleep_time.tv_sec = 0;
      sleep_time.tv_nsec = 0;

      if (micros >= 1e6) {
        sleep_time.tv_sec =
            std::min<int64>(micros / 1e6, std::numeric_limits<time_t>::max());
        micros -= static_cast<int64>(sleep_time.tv_sec) * 1e6;
      }
      if (micros < 1e6) {
        sleep_time.tv_nsec = 1000 * micros;
        micros = 0;
      }
      while (nanosleep(&sleep_time, &sleep_time) != 0 && errno == EINTR) {
        // Ignore signals and wait for the full interval to elapse.
      }
    }
  }

  Thread* StartThread(const ThreadOptions& thread_options, const string& name,
                      std::function<void()> fn) override {
    return new StdThread(thread_options, name, fn);
  }

  int32 GetCurrentThreadId() override {
#ifdef __APPLE__
    uint64_t tid64;
    pthread_threadid_np(nullptr, &tid64);
    return static_cast<int32>(tid64);
#elif defined(__FreeBSD__)
    return pthread_getthreadid_np();
#else
    return static_cast<int32>(pthread_self());
#endif
  }

  bool GetCurrentThreadName(string* name) override {
    {
      mutex_lock l(name_mutex);
      auto thread_name =
          GetThreadNameRegistry().find(std::this_thread::get_id());
      if (thread_name != GetThreadNameRegistry().end()) {
        *name = thread_name->second;
        return true;
      }
    }
#if defined(__ANDROID__) || defined(__EMSCRIPTEN__)
    return false;
#else
    char buf[100];
#ifdef __FreeBSD__
    int res = 0;
    pthread_get_name_np(pthread_self(), buf, static_cast<size_t>(100));
#else
    int res = pthread_getname_np(pthread_self(), buf, static_cast<size_t>(100));
#endif
    if (res != 0) {
      return false;
    }
    *name = buf;
    return true;
#endif
  }

  void SchedClosure(std::function<void()> closure) override {
    // TODO(b/27290852): Spawning a new thread here is wasteful, but
    // needed to deal with the fact that many `closure` functions are
    // blocking in the current codebase.
    std::thread closure_thread(closure);
    closure_thread.detach();
  }

  void SchedClosureAfter(int64 micros, std::function<void()> closure) override {
    // TODO(b/27290852): Consuming a thread here is wasteful, but this
    // code is (currently) only used in the case where a step fails
    // (AbortStep). This could be replaced by a timer thread
    SchedClosure([this, micros, closure]() {
      SleepForMicroseconds(micros);
      closure();
    });
  }

  Status LoadLibrary(const char* library_filename, void** handle) override {
    return tensorflow::internal::LoadLibrary(library_filename, handle);
  }

  Status GetSymbolFromLibrary(void* handle, const char* symbol_name,
                              void** symbol) override {
    return tensorflow::internal::GetSymbolFromLibrary(handle, symbol_name,
                                                      symbol);
  }

  string FormatLibraryFileName(const string& name,
                               const string& version) override {
    return tensorflow::internal::FormatLibraryFileName(name, version);
  }

  string GetRunfilesDir() override {
    string bin_path = this->GetExecutablePath();
    string runfiles_suffix = ".runfiles/org_tensorflow";
    std::size_t pos = bin_path.find(runfiles_suffix);

    // Sometimes (when executing under python) bin_path returns the full path to
    // the python scripts under runfiles. Get the substring.
    if (pos != std::string::npos) {
      return bin_path.substr(0, pos + runfiles_suffix.length());
    }

    // See if we have the executable path. if executable.runfiles exists, return
    // that folder.
    string runfiles_path = bin_path + runfiles_suffix;
    Status s = this->IsDirectory(runfiles_path);
    if (s.ok()) {
      return runfiles_path;
    }

    // If nothing can be found, return something close.
    return bin_path.substr(0, bin_path.find_last_of("/\\"));
  }

 private:
  void GetLocalTempDirectories(std::vector<string>* list) override;
};

}  // namespace

#if defined(PLATFORM_POSIX) || defined(__APPLE__) || defined(__ANDROID__)
REGISTER_FILE_SYSTEM("", PosixFileSystem);
REGISTER_FILE_SYSTEM("file", LocalPosixFileSystem);
REGISTER_FILE_SYSTEM("ram", RamFileSystem);

Env* Env::Default() {
  static Env* default_env = new PosixEnv;
  return default_env;
}
#endif

void PosixEnv::GetLocalTempDirectories(std::vector<string>* list) {
  list->clear();
  // Directories, in order of preference. If we find a dir that
  // exists, we stop adding other less-preferred dirs
  const char* candidates[] = {
    // Non-null only during unittest/regtest
    getenv("TEST_TMPDIR"),

    // Explicitly-supplied temp dirs
    getenv("TMPDIR"),
    getenv("TMP"),

#if defined(__ANDROID__)
    "/data/local/tmp",
#endif

    // If all else fails
    "/tmp",
  };

  for (const char* d : candidates) {
    if (!d || d[0] == '\0') continue;  // Empty env var

    // Make sure we don't surprise anyone who's expecting a '/'
    string dstr = d;
    if (dstr[dstr.size() - 1] != '/') {
      dstr += "/";
    }

    struct stat statbuf;
    if (!stat(d, &statbuf) && S_ISDIR(statbuf.st_mode) &&
        !access(dstr.c_str(), 0)) {
      // We found a dir that exists and is accessible - we're done.
      list->push_back(dstr);
      return;
    }
  }
}

int setenv(const char* name, const char* value, int overwrite) {
  return ::setenv(name, value, overwrite);
}

int unsetenv(const char* name) { return ::unsetenv(name); }

}  // namespace tensorflow
<commit_msg>Switch tensorflow::Env() to use pthread_create() to start threads instead of std::thread.<commit_after>/* Copyright 2015 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <fnmatch.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>

#ifdef __FreeBSD__
#include <pthread_np.h>
#endif

#include <thread>
#include <vector>

#include "tensorflow/core/platform/default/posix_file_system.h"
#include "tensorflow/core/platform/env.h"
#include "tensorflow/core/platform/load_library.h"
#include "tensorflow/core/platform/logging.h"
#include "tensorflow/core/platform/mutex.h"
#include "tensorflow/core/platform/ram_file_system.h"
#include "tensorflow/core/protobuf/error_codes.pb.h"

namespace tensorflow {

namespace {

mutex name_mutex(tensorflow::LINKER_INITIALIZED);

std::map<std::thread::id, string>& GetThreadNameRegistry()
    TF_EXCLUSIVE_LOCKS_REQUIRED(name_mutex) {
  static auto* thread_name_registry = new std::map<std::thread::id, string>();
  return *thread_name_registry;
}

// We use the pthread API instead of std::thread so we can control stack sizes.
class PThread : public Thread {
 public:
  PThread(const ThreadOptions& thread_options, const std::string& name,
          std::function<void()> fn) {
    ThreadParams* params = new ThreadParams;
    params->name = name;
    params->fn = std::move(fn);
    pthread_attr_t attributes;
    pthread_attr_init(&attributes);
    if (thread_options.stack_size != 0) {
      pthread_attr_setstacksize(&attributes, thread_options.stack_size);
    }
    int ret = pthread_create(&thread_, &attributes, &ThreadFn, params);
    // There is no mechanism for the thread creation API to fail, so we CHECK.
    CHECK_EQ(ret, 0) << "Thread creation via pthread_create() failed.";
    pthread_attr_destroy(&attributes);
  }

  ~PThread() override { pthread_join(thread_, nullptr); }

 private:
  struct ThreadParams {
    std::string name;
    std::function<void()> fn;
  };
  static void* ThreadFn(void* params_arg) {
    std::unique_ptr<ThreadParams> params(
        reinterpret_cast<ThreadParams*>(params_arg));
    {
      mutex_lock l(name_mutex);
      GetThreadNameRegistry().emplace(std::this_thread::get_id(), params->name);
    }
    params->fn();
    {
      mutex_lock l(name_mutex);
      GetThreadNameRegistry().erase(std::this_thread::get_id());
    }
    return nullptr;
  }

  pthread_t thread_;
};

class PosixEnv : public Env {
 public:
  PosixEnv() {}

  ~PosixEnv() override { LOG(FATAL) << "Env::Default() must not be destroyed"; }

  bool MatchPath(const string& path, const string& pattern) override {
    return fnmatch(pattern.c_str(), path.c_str(), FNM_PATHNAME) == 0;
  }

  void SleepForMicroseconds(int64 micros) override {
    while (micros > 0) {
      timespec sleep_time;
      sleep_time.tv_sec = 0;
      sleep_time.tv_nsec = 0;

      if (micros >= 1e6) {
        sleep_time.tv_sec =
            std::min<int64>(micros / 1e6, std::numeric_limits<time_t>::max());
        micros -= static_cast<int64>(sleep_time.tv_sec) * 1e6;
      }
      if (micros < 1e6) {
        sleep_time.tv_nsec = 1000 * micros;
        micros = 0;
      }
      while (nanosleep(&sleep_time, &sleep_time) != 0 && errno == EINTR) {
        // Ignore signals and wait for the full interval to elapse.
      }
    }
  }

  Thread* StartThread(const ThreadOptions& thread_options, const string& name,
                      std::function<void()> fn) override {
    return new PThread(thread_options, name, fn);
  }

  int32 GetCurrentThreadId() override {
#ifdef __APPLE__
    uint64_t tid64;
    pthread_threadid_np(nullptr, &tid64);
    return static_cast<int32>(tid64);
#elif defined(__FreeBSD__)
    return pthread_getthreadid_np();
#else
    return static_cast<int32>(pthread_self());
#endif
  }

  bool GetCurrentThreadName(string* name) override {
    {
      mutex_lock l(name_mutex);
      auto thread_name =
          GetThreadNameRegistry().find(std::this_thread::get_id());
      if (thread_name != GetThreadNameRegistry().end()) {
        *name = thread_name->second;
        return true;
      }
    }
#if defined(__ANDROID__) || defined(__EMSCRIPTEN__)
    return false;
#else
    char buf[100];
#ifdef __FreeBSD__
    int res = 0;
    pthread_get_name_np(pthread_self(), buf, static_cast<size_t>(100));
#else
    int res = pthread_getname_np(pthread_self(), buf, static_cast<size_t>(100));
#endif
    if (res != 0) {
      return false;
    }
    *name = buf;
    return true;
#endif
  }

  void SchedClosure(std::function<void()> closure) override {
    // TODO(b/27290852): Spawning a new thread here is wasteful, but
    // needed to deal with the fact that many `closure` functions are
    // blocking in the current codebase.
    std::thread closure_thread(closure);
    closure_thread.detach();
  }

  void SchedClosureAfter(int64 micros, std::function<void()> closure) override {
    // TODO(b/27290852): Consuming a thread here is wasteful, but this
    // code is (currently) only used in the case where a step fails
    // (AbortStep). This could be replaced by a timer thread
    SchedClosure([this, micros, closure]() {
      SleepForMicroseconds(micros);
      closure();
    });
  }

  Status LoadLibrary(const char* library_filename, void** handle) override {
    return tensorflow::internal::LoadLibrary(library_filename, handle);
  }

  Status GetSymbolFromLibrary(void* handle, const char* symbol_name,
                              void** symbol) override {
    return tensorflow::internal::GetSymbolFromLibrary(handle, symbol_name,
                                                      symbol);
  }

  string FormatLibraryFileName(const string& name,
                               const string& version) override {
    return tensorflow::internal::FormatLibraryFileName(name, version);
  }

  string GetRunfilesDir() override {
    string bin_path = this->GetExecutablePath();
    string runfiles_suffix = ".runfiles/org_tensorflow";
    std::size_t pos = bin_path.find(runfiles_suffix);

    // Sometimes (when executing under python) bin_path returns the full path to
    // the python scripts under runfiles. Get the substring.
    if (pos != std::string::npos) {
      return bin_path.substr(0, pos + runfiles_suffix.length());
    }

    // See if we have the executable path. if executable.runfiles exists, return
    // that folder.
    string runfiles_path = bin_path + runfiles_suffix;
    Status s = this->IsDirectory(runfiles_path);
    if (s.ok()) {
      return runfiles_path;
    }

    // If nothing can be found, return something close.
    return bin_path.substr(0, bin_path.find_last_of("/\\"));
  }

 private:
  void GetLocalTempDirectories(std::vector<string>* list) override;
};

}  // namespace

#if defined(PLATFORM_POSIX) || defined(__APPLE__) || defined(__ANDROID__)
REGISTER_FILE_SYSTEM("", PosixFileSystem);
REGISTER_FILE_SYSTEM("file", LocalPosixFileSystem);
REGISTER_FILE_SYSTEM("ram", RamFileSystem);

Env* Env::Default() {
  static Env* default_env = new PosixEnv;
  return default_env;
}
#endif

void PosixEnv::GetLocalTempDirectories(std::vector<string>* list) {
  list->clear();
  // Directories, in order of preference. If we find a dir that
  // exists, we stop adding other less-preferred dirs
  const char* candidates[] = {
    // Non-null only during unittest/regtest
    getenv("TEST_TMPDIR"),

    // Explicitly-supplied temp dirs
    getenv("TMPDIR"),
    getenv("TMP"),

#if defined(__ANDROID__)
    "/data/local/tmp",
#endif

    // If all else fails
    "/tmp",
  };

  for (const char* d : candidates) {
    if (!d || d[0] == '\0') continue;  // Empty env var

    // Make sure we don't surprise anyone who's expecting a '/'
    string dstr = d;
    if (dstr[dstr.size() - 1] != '/') {
      dstr += "/";
    }

    struct stat statbuf;
    if (!stat(d, &statbuf) && S_ISDIR(statbuf.st_mode) &&
        !access(dstr.c_str(), 0)) {
      // We found a dir that exists and is accessible - we're done.
      list->push_back(dstr);
      return;
    }
  }
}

int setenv(const char* name, const char* value, int overwrite) {
  return ::setenv(name, value, overwrite);
}

int unsetenv(const char* name) { return ::unsetenv(name); }

}  // namespace tensorflow
<|endoftext|>