<commit_before>/*

Copyright 2015-2019 Igor Petrovic

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 "board/Board.h"
#include "board/common/constants/LEDs.h"
#include "board/common/digital/Output.h"
#include "board/common/Map.h"
#include "core/src/general/BitManipulation.h"
#include "Pins.h"
#include "interface/digital/output/leds/Helpers.h"
#ifndef BOARD_A_xu2
#include "interface/digital/output/leds/LEDs.h"
#endif

#ifdef LED_INDICATORS
namespace
{
    ///
    /// \brief Variables used to control the time MIDI in/out LED indicators on board are active.
    /// When these LEDs need to be turned on, variables are set to value representing time in
    /// milliseconds during which they should be on. ISR decreases variable value by 1 every 1 millsecond.
    /// Once the variables have value 0, specific LED indicator is turned off.
    /// @{

    volatile uint8_t midiIn_timeout;
    volatile uint8_t midiOut_timeout;

    /// @}
}    // namespace
#endif

#ifndef BOARD_A_xu2
namespace
{
    volatile uint8_t pwmSteps;
    volatile int8_t  transitionCounter[MAX_NUMBER_OF_LEDS];

    ///
    /// \brief Array holding values needed to achieve more natural LED transition between states.
    ///
    const uint8_t ledTransitionScale[NUMBER_OF_LED_TRANSITIONS] = {
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        1,
        1,
        1,
        1,
        2,
        2,
        2,
        2,
        3,
        3,
        4,
        4,
        5,
        5,
        6,
        6,
        7,
        8,
        9,
        10,
        11,
        12,
        13,
        14,
        16,
        17,
        19,
        21,
        23,
        25,
        28,
        30,
        33,
        36,
        40,
        44,
        48,
        52,
        57,
        62,
        68,
        74,
        81,
        89,
        97,
        106,
        115,
        126,
        138,
        150,
        164,
        179,
        195,
        213,
        255
    };

#ifdef NUMBER_OF_LED_COLUMNS
    ///
    /// \brief Holds value of currently active output matrix column.
    ///
    volatile uint8_t activeOutColumn;
#endif
}    // namespace
#endif

namespace Board
{
    namespace interface
    {
        namespace digital
        {
            namespace output
            {
#ifndef BOARD_A_xu2
#ifdef LEDS_SUPPORTED
                bool setLEDfadeSpeed(uint8_t transitionSpeed)
                {
                    if (transitionSpeed > FADE_TIME_MAX)
                    {
                        return false;
                    }

//reset transition counter
#ifdef __AVR__
                    ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
#endif
                    {
                        for (int i = 0; i < MAX_NUMBER_OF_LEDS; i++)
                            transitionCounter[i] = 0;

                        pwmSteps = transitionSpeed;
                    }

                    return true;
                }

                uint8_t getRGBaddress(uint8_t rgbID, Interface::digital::output::LEDs::rgbIndex_t index)
                {
#ifdef NUMBER_OF_LED_COLUMNS
                    uint8_t column = rgbID % NUMBER_OF_LED_COLUMNS;
                    uint8_t row = (rgbID / NUMBER_OF_LED_COLUMNS) * 3;
                    uint8_t address = column + NUMBER_OF_LED_COLUMNS * row;

                    switch (index)
                    {
                    case Interface::digital::output::LEDs::rgbIndex_t::r:
                        return address;

                    case Interface::digital::output::LEDs::rgbIndex_t::g:
                        return address + NUMBER_OF_LED_COLUMNS * 1;

                    case Interface::digital::output::LEDs::rgbIndex_t::b:
                        return address + NUMBER_OF_LED_COLUMNS * 2;
                    }

                    return 0;
#else
                    return rgbID * 3 + static_cast<uint8_t>(index);
#endif
                }

                uint8_t getRGBID(uint8_t ledID)
                {
#ifdef NUMBER_OF_LED_COLUMNS
                    uint8_t row = ledID / NUMBER_OF_LED_COLUMNS;

                    uint8_t mod = row % 3;
                    row -= mod;

                    uint8_t column = ledID % NUMBER_OF_LED_COLUMNS;

                    uint8_t result = (row * NUMBER_OF_LED_COLUMNS) / 3 + column;

                    if (result >= MAX_NUMBER_OF_RGB_LEDS)
                        return MAX_NUMBER_OF_RGB_LEDS - 1;
                    else
                        return result;
#else
                    uint8_t result = ledID / 3;

                    if (result >= MAX_NUMBER_OF_RGB_LEDS)
                        return MAX_NUMBER_OF_RGB_LEDS - 1;
                    else
                        return result;
#endif
                }
#endif
#endif

                namespace detail
                {
#ifdef LED_INDICATORS
                    void checkIndicators()
                    {
                        if (Board::USB::trafficIndicator.received || Board::UART::trafficIndicator.received)
                        {
                            INT_LED_ON(LED_IN_PORT, LED_IN_PIN);
                            Board::USB::trafficIndicator.received = false;
                            Board::UART::trafficIndicator.received = false;
                            midiIn_timeout = MIDI_INDICATOR_TIMEOUT;
                        }

                        if (midiIn_timeout)
                            midiIn_timeout--;
                        else
                            INT_LED_OFF(LED_IN_PORT, LED_IN_PIN);

                        if (Board::USB::trafficIndicator.sent || Board::UART::trafficIndicator.sent)
                        {
                            INT_LED_ON(LED_OUT_PORT, LED_OUT_PIN);
                            Board::USB::trafficIndicator.sent = false;
                            Board::UART::trafficIndicator.sent = false;
                            midiOut_timeout = MIDI_INDICATOR_TIMEOUT;
                        }

                        if (midiOut_timeout)
                            midiOut_timeout--;
                        else
                            INT_LED_OFF(LED_OUT_PORT, LED_OUT_PIN);
                    }
#endif

#ifndef BOARD_A_xu2
#ifdef NUMBER_OF_LED_COLUMNS
                    ///
                    /// \brief Switches to next LED matrix column.
                    ///
                    inline void activateOutputColumn()
                    {
                        BIT_READ(activeOutColumn, 0) ? CORE_AVR_PIN_SET_HIGH(DEC_LM_A0_PORT, DEC_LM_A0_PIN) : CORE_AVR_PIN_SET_LOW(DEC_LM_A0_PORT, DEC_LM_A0_PIN);
                        BIT_READ(activeOutColumn, 1) ? CORE_AVR_PIN_SET_HIGH(DEC_LM_A1_PORT, DEC_LM_A1_PIN) : CORE_AVR_PIN_SET_LOW(DEC_LM_A1_PORT, DEC_LM_A1_PIN);
                        BIT_READ(activeOutColumn, 2) ? CORE_AVR_PIN_SET_HIGH(DEC_LM_A2_PORT, DEC_LM_A2_PIN) : CORE_AVR_PIN_SET_LOW(DEC_LM_A2_PORT, DEC_LM_A2_PIN);
                    }

                    namespace
                    {
                        core::CORE_ARCH::pins::mcuPin_t pin;
                        uint8_t                         ledNumber;
                        uint8_t                         ledStateSingle;
                    }    // namespace

                    ///
                    /// \brief Used to turn the given LED row off.
                    ///
                    inline void ledRowOff(uint8_t row)
                    {
#ifdef LED_FADING
                        //turn off pwm
                        core::CORE_ARCH::pins::pwmOff(Board::map::pwmChannel(row));
#endif

                        pin = Board::map::led(row);
                        EXT_LED_OFF(*pin.port, pin.pin);
                    }

                    ///
                    /// \brief Used to turn the given LED row on.
                    /// If led fading is supported on board, intensity must be specified as well.
                    ///
                    inline void ledRowOn(uint8_t row
#ifdef LED_FADING
                                         ,
                                         uint8_t intensity
#endif
                    )
                    {
#ifdef LED_FADING
                        if (intensity == 255)
#endif
                        {
                            pin = Board::map::led(row);

                            //max value, don't use pwm
                            EXT_LED_ON(*pin.port, pin.pin);
                        }
#ifdef LED_FADING
                        else
                        {
#ifdef LED_EXT_INVERT
                            intensity = 255 - intensity;
#endif

                            core::CORE_ARCH::pins::pwmOn(Board::map::pwmChannel(row), intensity);
                        }
#endif
                    }

                    void checkDigitalOutputs()
                    {
                        for (int i = 0; i < NUMBER_OF_LED_ROWS; i++)
                            ledRowOff(i);

                        activateOutputColumn();

                        //if there is an active LED in current column, turn on LED row
                        //do fancy transitions here
                        for (int i = 0; i < NUMBER_OF_LED_ROWS; i++)
                        {
                            ledNumber = activeOutColumn + i * NUMBER_OF_LED_COLUMNS;
                            ledStateSingle = LED_ON(Interface::digital::output::LEDs::getLEDstate(ledNumber));

                            ledStateSingle *= (NUMBER_OF_LED_TRANSITIONS - 1);

                            //don't bother with pwm if it's disabled
                            if (!pwmSteps && ledStateSingle)
                            {
                                ledRowOn(i
#ifdef LED_FADING
                                         ,
                                         255
#endif
                                );
                            }
                            else
                            {
                                if (ledTransitionScale[transitionCounter[ledNumber]])
                                    ledRowOn(i
#ifdef LED_FADING
                                             ,
                                             ledTransitionScale[transitionCounter[ledNumber]]
#endif
                                    );

                                if (transitionCounter[ledNumber] != ledStateSingle)
                                {
                                    //fade up
                                    if (transitionCounter[ledNumber] < ledStateSingle)
                                    {
                                        transitionCounter[ledNumber] += pwmSteps;

                                        if (transitionCounter[ledNumber] > ledStateSingle)
                                            transitionCounter[ledNumber] = ledStateSingle;
                                    }
                                    else
                                    {
                                        //fade down
                                        transitionCounter[ledNumber] -= pwmSteps;

                                        if (transitionCounter[ledNumber] < 0)
                                            transitionCounter[ledNumber] = 0;
                                    }
                                }
                            }
                        }

                        if (++activeOutColumn == NUMBER_OF_LED_COLUMNS)
                            activeOutColumn = 0;
                    }
#else
                    namespace
                    {
                        uint8_t lastLEDstate[MAX_NUMBER_OF_LEDS];
                        uint8_t ledStateSingle;
                    }    // namespace

#ifdef NUMBER_OF_OUT_SR
                    ///
                    /// \brief Checks if any LED state has been changed and writes changed state to output shift registers.
                    ///
                    void checkDigitalOutputs()
                    {
                        bool updateSR = false;

                        for (int i = 0; i < MAX_NUMBER_OF_LEDS; i++)
                        {
                            ledStateSingle = LED_ON(Interface::digital::output::LEDs::getLEDstate(i));

                            if (ledStateSingle != lastLEDstate[i])
                            {
                                lastLEDstate[i] = ledStateSingle;
                                updateSR = true;
                            }
                        }

                        if (updateSR)
                        {
                            CORE_AVR_PIN_SET_LOW(SR_OUT_LATCH_PORT, SR_OUT_LATCH_PIN);

                            for (int i = 0; i < MAX_NUMBER_OF_LEDS; i++)
                            {
                                LED_ON(Interface::digital::output::LEDs::getLEDstate(i)) ? EXT_LED_ON(SR_OUT_DATA_PORT, SR_OUT_DATA_PIN) : EXT_LED_OFF(SR_OUT_DATA_PORT, SR_OUT_DATA_PIN);
                                CORE_AVR_PIN_SET_HIGH(SR_OUT_CLK_PORT, SR_OUT_CLK_PIN);
                                _NOP();
                                _NOP();
                                CORE_AVR_PIN_SET_LOW(SR_OUT_CLK_PORT, SR_OUT_CLK_PIN);
                            }

                            CORE_AVR_PIN_SET_HIGH(SR_OUT_LATCH_PORT, SR_OUT_LATCH_PIN);
                        }
                    }
#else
                    namespace
                    {
                        core::CORE_ARCH::pins::mcuPin_t pin;
                    }

                    void checkDigitalOutputs()
                    {
                        for (int i = 0; i < MAX_NUMBER_OF_LEDS; i++)
                        {
                            ledStateSingle = LED_ON(Interface::digital::output::LEDs::getLEDstate(i));
                            pin = Board::map::led(i);

                            if (ledStateSingle != lastLEDstate[i])
                            {
                                if (ledStateSingle)
                                    EXT_LED_ON(*pin.port, pin.pin);
                                else
                                    EXT_LED_OFF(*pin.port, pin.pin);

                                lastLEDstate[i] = ledStateSingle;
                            }
                        }
                    }
#endif
#endif
#endif
                }    // namespace detail
            }        // namespace output
        }            // namespace digital
    }                // namespace interface
}    // namespace Board<commit_msg>fix 74HC595 handling<commit_after>/*

Copyright 2015-2019 Igor Petrovic

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 "board/Board.h"
#include "board/common/constants/LEDs.h"
#include "board/common/digital/Output.h"
#include "board/common/Map.h"
#include "core/src/general/BitManipulation.h"
#include "Pins.h"
#include "interface/digital/output/leds/Helpers.h"
#ifndef BOARD_A_xu2
#include "interface/digital/output/leds/LEDs.h"
#endif

#ifdef LED_INDICATORS
namespace
{
    ///
    /// \brief Variables used to control the time MIDI in/out LED indicators on board are active.
    /// When these LEDs need to be turned on, variables are set to value representing time in
    /// milliseconds during which they should be on. ISR decreases variable value by 1 every 1 millsecond.
    /// Once the variables have value 0, specific LED indicator is turned off.
    /// @{

    volatile uint8_t midiIn_timeout;
    volatile uint8_t midiOut_timeout;

    /// @}
}    // namespace
#endif

#ifndef BOARD_A_xu2
namespace
{
    volatile uint8_t pwmSteps;
    volatile int8_t  transitionCounter[MAX_NUMBER_OF_LEDS];

    ///
    /// \brief Array holding values needed to achieve more natural LED transition between states.
    ///
    const uint8_t ledTransitionScale[NUMBER_OF_LED_TRANSITIONS] = {
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        1,
        1,
        1,
        1,
        2,
        2,
        2,
        2,
        3,
        3,
        4,
        4,
        5,
        5,
        6,
        6,
        7,
        8,
        9,
        10,
        11,
        12,
        13,
        14,
        16,
        17,
        19,
        21,
        23,
        25,
        28,
        30,
        33,
        36,
        40,
        44,
        48,
        52,
        57,
        62,
        68,
        74,
        81,
        89,
        97,
        106,
        115,
        126,
        138,
        150,
        164,
        179,
        195,
        213,
        255
    };

#ifdef NUMBER_OF_LED_COLUMNS
    ///
    /// \brief Holds value of currently active output matrix column.
    ///
    volatile uint8_t activeOutColumn;
#endif
}    // namespace
#endif

namespace Board
{
    namespace interface
    {
        namespace digital
        {
            namespace output
            {
#ifndef BOARD_A_xu2
#ifdef LEDS_SUPPORTED
                bool setLEDfadeSpeed(uint8_t transitionSpeed)
                {
                    if (transitionSpeed > FADE_TIME_MAX)
                    {
                        return false;
                    }

//reset transition counter
#ifdef __AVR__
                    ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
#endif
                    {
                        for (int i = 0; i < MAX_NUMBER_OF_LEDS; i++)
                            transitionCounter[i] = 0;

                        pwmSteps = transitionSpeed;
                    }

                    return true;
                }

                uint8_t getRGBaddress(uint8_t rgbID, Interface::digital::output::LEDs::rgbIndex_t index)
                {
#ifdef NUMBER_OF_LED_COLUMNS
                    uint8_t column = rgbID % NUMBER_OF_LED_COLUMNS;
                    uint8_t row = (rgbID / NUMBER_OF_LED_COLUMNS) * 3;
                    uint8_t address = column + NUMBER_OF_LED_COLUMNS * row;

                    switch (index)
                    {
                    case Interface::digital::output::LEDs::rgbIndex_t::r:
                        return address;

                    case Interface::digital::output::LEDs::rgbIndex_t::g:
                        return address + NUMBER_OF_LED_COLUMNS * 1;

                    case Interface::digital::output::LEDs::rgbIndex_t::b:
                        return address + NUMBER_OF_LED_COLUMNS * 2;
                    }

                    return 0;
#else
                    return rgbID * 3 + static_cast<uint8_t>(index);
#endif
                }

                uint8_t getRGBID(uint8_t ledID)
                {
#ifdef NUMBER_OF_LED_COLUMNS
                    uint8_t row = ledID / NUMBER_OF_LED_COLUMNS;

                    uint8_t mod = row % 3;
                    row -= mod;

                    uint8_t column = ledID % NUMBER_OF_LED_COLUMNS;

                    uint8_t result = (row * NUMBER_OF_LED_COLUMNS) / 3 + column;

                    if (result >= MAX_NUMBER_OF_RGB_LEDS)
                        return MAX_NUMBER_OF_RGB_LEDS - 1;
                    else
                        return result;
#else
                    uint8_t result = ledID / 3;

                    if (result >= MAX_NUMBER_OF_RGB_LEDS)
                        return MAX_NUMBER_OF_RGB_LEDS - 1;
                    else
                        return result;
#endif
                }
#endif
#endif

                namespace detail
                {
#ifdef LED_INDICATORS
                    void checkIndicators()
                    {
                        if (Board::USB::trafficIndicator.received || Board::UART::trafficIndicator.received)
                        {
                            INT_LED_ON(LED_IN_PORT, LED_IN_PIN);
                            Board::USB::trafficIndicator.received = false;
                            Board::UART::trafficIndicator.received = false;
                            midiIn_timeout = MIDI_INDICATOR_TIMEOUT;
                        }

                        if (midiIn_timeout)
                            midiIn_timeout--;
                        else
                            INT_LED_OFF(LED_IN_PORT, LED_IN_PIN);

                        if (Board::USB::trafficIndicator.sent || Board::UART::trafficIndicator.sent)
                        {
                            INT_LED_ON(LED_OUT_PORT, LED_OUT_PIN);
                            Board::USB::trafficIndicator.sent = false;
                            Board::UART::trafficIndicator.sent = false;
                            midiOut_timeout = MIDI_INDICATOR_TIMEOUT;
                        }

                        if (midiOut_timeout)
                            midiOut_timeout--;
                        else
                            INT_LED_OFF(LED_OUT_PORT, LED_OUT_PIN);
                    }
#endif

#ifndef BOARD_A_xu2
#ifdef NUMBER_OF_LED_COLUMNS
                    ///
                    /// \brief Switches to next LED matrix column.
                    ///
                    inline void activateOutputColumn()
                    {
                        BIT_READ(activeOutColumn, 0) ? CORE_AVR_PIN_SET_HIGH(DEC_LM_A0_PORT, DEC_LM_A0_PIN) : CORE_AVR_PIN_SET_LOW(DEC_LM_A0_PORT, DEC_LM_A0_PIN);
                        BIT_READ(activeOutColumn, 1) ? CORE_AVR_PIN_SET_HIGH(DEC_LM_A1_PORT, DEC_LM_A1_PIN) : CORE_AVR_PIN_SET_LOW(DEC_LM_A1_PORT, DEC_LM_A1_PIN);
                        BIT_READ(activeOutColumn, 2) ? CORE_AVR_PIN_SET_HIGH(DEC_LM_A2_PORT, DEC_LM_A2_PIN) : CORE_AVR_PIN_SET_LOW(DEC_LM_A2_PORT, DEC_LM_A2_PIN);
                    }

                    namespace
                    {
                        core::CORE_ARCH::pins::mcuPin_t pin;
                        uint8_t                         ledNumber;
                        uint8_t                         ledStateSingle;
                    }    // namespace

                    ///
                    /// \brief Used to turn the given LED row off.
                    ///
                    inline void ledRowOff(uint8_t row)
                    {
#ifdef LED_FADING
                        //turn off pwm
                        core::CORE_ARCH::pins::pwmOff(Board::map::pwmChannel(row));
#endif

                        pin = Board::map::led(row);
                        EXT_LED_OFF(*pin.port, pin.pin);
                    }

                    ///
                    /// \brief Used to turn the given LED row on.
                    /// If led fading is supported on board, intensity must be specified as well.
                    ///
                    inline void ledRowOn(uint8_t row
#ifdef LED_FADING
                                         ,
                                         uint8_t intensity
#endif
                    )
                    {
#ifdef LED_FADING
                        if (intensity == 255)
#endif
                        {
                            pin = Board::map::led(row);

                            //max value, don't use pwm
                            EXT_LED_ON(*pin.port, pin.pin);
                        }
#ifdef LED_FADING
                        else
                        {
#ifdef LED_EXT_INVERT
                            intensity = 255 - intensity;
#endif

                            core::CORE_ARCH::pins::pwmOn(Board::map::pwmChannel(row), intensity);
                        }
#endif
                    }

                    void checkDigitalOutputs()
                    {
                        for (int i = 0; i < NUMBER_OF_LED_ROWS; i++)
                            ledRowOff(i);

                        activateOutputColumn();

                        //if there is an active LED in current column, turn on LED row
                        //do fancy transitions here
                        for (int i = 0; i < NUMBER_OF_LED_ROWS; i++)
                        {
                            ledNumber = activeOutColumn + i * NUMBER_OF_LED_COLUMNS;
                            ledStateSingle = LED_ON(Interface::digital::output::LEDs::getLEDstate(ledNumber));

                            ledStateSingle *= (NUMBER_OF_LED_TRANSITIONS - 1);

                            //don't bother with pwm if it's disabled
                            if (!pwmSteps && ledStateSingle)
                            {
                                ledRowOn(i
#ifdef LED_FADING
                                         ,
                                         255
#endif
                                );
                            }
                            else
                            {
                                if (ledTransitionScale[transitionCounter[ledNumber]])
                                    ledRowOn(i
#ifdef LED_FADING
                                             ,
                                             ledTransitionScale[transitionCounter[ledNumber]]
#endif
                                    );

                                if (transitionCounter[ledNumber] != ledStateSingle)
                                {
                                    //fade up
                                    if (transitionCounter[ledNumber] < ledStateSingle)
                                    {
                                        transitionCounter[ledNumber] += pwmSteps;

                                        if (transitionCounter[ledNumber] > ledStateSingle)
                                            transitionCounter[ledNumber] = ledStateSingle;
                                    }
                                    else
                                    {
                                        //fade down
                                        transitionCounter[ledNumber] -= pwmSteps;

                                        if (transitionCounter[ledNumber] < 0)
                                            transitionCounter[ledNumber] = 0;
                                    }
                                }
                            }
                        }

                        if (++activeOutColumn == NUMBER_OF_LED_COLUMNS)
                            activeOutColumn = 0;
                    }
#else
                    namespace
                    {
                        uint8_t lastLEDstate[MAX_NUMBER_OF_LEDS];
                        uint8_t ledStateSingle;
                    }    // namespace

#ifdef NUMBER_OF_OUT_SR
                    ///
                    /// \brief Checks if any LED state has been changed and writes changed state to output shift registers.
                    ///
                    void checkDigitalOutputs()
                    {
                        bool updateSR = false;

                        for (int i = 0; i < MAX_NUMBER_OF_LEDS; i++)
                        {
                            ledStateSingle = LED_ON(Interface::digital::output::LEDs::getLEDstate(i));

                            if (ledStateSingle != lastLEDstate[i])
                            {
                                lastLEDstate[i] = ledStateSingle;
                                updateSR = true;
                            }
                        }

                        if (updateSR)
                        {
                            CORE_AVR_PIN_SET_LOW(SR_OUT_LATCH_PORT, SR_OUT_LATCH_PIN);

                            for (int i = 0; i < MAX_NUMBER_OF_LEDS; i++)
                            {
                                LED_ON(Interface::digital::output::LEDs::getLEDstate(i)) ? EXT_LED_ON(SR_OUT_DATA_PORT, SR_OUT_DATA_PIN) : EXT_LED_OFF(SR_OUT_DATA_PORT, SR_OUT_DATA_PIN);
                                CORE_AVR_PIN_SET_LOW(SR_OUT_CLK_PORT, SR_OUT_CLK_PIN);
                                _NOP();
                                _NOP();
                                CORE_AVR_PIN_SET_HIGH(SR_OUT_CLK_PORT, SR_OUT_CLK_PIN);
                            }

                            CORE_AVR_PIN_SET_HIGH(SR_OUT_LATCH_PORT, SR_OUT_LATCH_PIN);
                        }
                    }
#else
                    namespace
                    {
                        core::CORE_ARCH::pins::mcuPin_t pin;
                    }

                    void checkDigitalOutputs()
                    {
                        for (int i = 0; i < MAX_NUMBER_OF_LEDS; i++)
                        {
                            ledStateSingle = LED_ON(Interface::digital::output::LEDs::getLEDstate(i));
                            pin = Board::map::led(i);

                            if (ledStateSingle != lastLEDstate[i])
                            {
                                if (ledStateSingle)
                                    EXT_LED_ON(*pin.port, pin.pin);
                                else
                                    EXT_LED_OFF(*pin.port, pin.pin);

                                lastLEDstate[i] = ledStateSingle;
                            }
                        }
                    }
#endif
#endif
#endif
                }    // namespace detail
            }        // namespace output
        }            // namespace digital
    }                // namespace interface
}    // namespace Board<|endoftext|>


<commit_before>#include "arma3client.hpp"

#include <algorithm>
#include <filesystem>
#include <string>

#include <fmt/format.h>
#include <fmt/ostream.h>

#include <static_todo.hpp>

#include "cppfilter.hpp"
#include "filesystem_utils.hpp"
#include "steam_utils.hpp"
#include "std_utils.hpp"
#include "string_utils.hpp"

#include "exceptions/directory_not_found.hpp"
#include "exceptions/file_no_access.hpp"
#include "exceptions/file_not_found.hpp"
#include "exceptions/not_a_symlink.hpp"
#include "exceptions/syntax_error.hpp"

using namespace std;
using namespace std::filesystem;

using namespace StdUtils;
using namespace StringUtils;

namespace fs = FilesystemUtils;

#ifdef __linux
namespace
{
    std::string get_esync_prefix(bool disable_esync)
    {
        if (disable_esync)
            return "PROTON_NO_ESYNC=1";
        return "";
    }

    std::string optional_steam_runtime(SteamUtils const &steam_utils)
    {
        if (StdUtils::IsLibraryAvailable("libpng12.so"))
            return "";
        auto const steam_runtime_path = steam_utils.GetSteamPath() / "ubuntu12_32/steam-runtime/run.sh";
        if (fs::Exists(steam_runtime_path))
            return steam_runtime_path;
        fmt::print(stderr, "Did not find {} and libpng12.so is missing. Thermal optics will be broken!\n", steam_runtime_path);
        return "";
    }

    std::string get_verb(std::string const &arguments)
    {
        return StringUtils::Replace(arguments, "%verb%", "run");;
    }

    void direct_launch(std::filesystem::path const &arma_path, std::filesystem::path const &executable_path,
                       string const &arguments, bool is_proton, string const &user_environment_variables, bool disable_esync)
    {
        if (!is_proton)
        {
            StdUtils::StartBackgroundProcess(fmt::format("env {} {} {}", user_environment_variables, executable_path, arguments),
                                             arma_path.string());
            return;
        }

        try
        {
            SteamUtils steam_utils;
            auto const arma3_id = std::stoull(ARMA3::Definitions::app_id);
            auto const compatibility_tool = steam_utils.GetCompatibilityToolForAppId(arma3_id);

            auto ld_preload_path = fmt::format("{}/ubuntu12_64/gameoverlayrenderer.so", steam_utils.GetSteamPath().string());
            if (char const *old_ld_preload = getenv("LD_PRELOAD"); old_ld_preload)
                ld_preload_path = fmt::format("{}:{}", ld_preload_path, old_ld_preload);

            auto const steam_compat_data_path = steam_utils.GetInstallPathFromGamePath(arma_path) / "steamapps/compatdata" /
                                                ARMA3::Definitions::app_id;

            auto const environment = fmt::format(R"env({} SteamGameId={} LD_PRELOAD={} STEAM_COMPAT_DATA_PATH="{}")env",
                                                 get_esync_prefix(disable_esync), arma3_id, ld_preload_path, steam_compat_data_path.string());
            auto const command = fmt::format(R"command(env {} {} {} {} {} "{}" {})command", environment, user_environment_variables,
                                             optional_steam_runtime(steam_utils), compatibility_tool.first,
                                             get_verb(compatibility_tool.second), executable_path.string(), arguments);
            fmt::print("Running Arma:\n{}\n", command);
            StdUtils::StartBackgroundProcess(command, arma_path.string());
        }
        catch (std::exception const &e)
        {
            fmt::print(stderr, "Direct launch failed, exception: {}\n", e.what());
        }
    }

    void indirect_flatpak_launch(string const &arguments, bool is_proton, string const &user_environment_variables,
                                 bool disable_esync)
    {
        if (is_proton)
            StdUtils::StartBackgroundProcess(
                fmt::format("flatpak run --env=\"{} {}\" com.valvesoftware.Steam -applaunch 107410 -nolauncher {}",
                            get_esync_prefix(disable_esync), user_environment_variables, arguments));
        else
            StdUtils::StartBackgroundProcess(fmt::format("flatpak run com.valvesoftware.Steam -applaunch 107410 -nolauncher {}",
                                             arguments));
    }

    void indirect_launch(string const &arguments, bool is_proton, string const &user_environment_variables,
                         bool disable_esync)
    {
        if (is_proton)
            StdUtils::StartBackgroundProcess(fmt::format("env {} {} steam -applaunch 107410 -nolauncher {}",
                                             get_esync_prefix(disable_esync), user_environment_variables, arguments));
        else
            StdUtils::StartBackgroundProcess(fmt::format("env {} steam -applaunch 107410 {}", user_environment_variables,
                                             arguments));
    }

    void indirect_launch_through_steam(string const &arguments, bool is_proton, string const &user_environment_variables,
                                       bool disable_esync, bool is_flatpak)
    {
        if (is_flatpak)
            indirect_flatpak_launch(arguments, is_proton, user_environment_variables, disable_esync);
        else
            indirect_launch(arguments, is_proton, user_environment_variables, disable_esync);
    }
}
#endif

namespace ARMA3
{
    Client::Client(std::filesystem::path const &arma_path, std::filesystem::path const &target_workshop_path)
    {
        path_ = arma_path;
        for (auto const &executable : Definitions::executable_names)
        {
            if (fs::Exists(path_ / executable))
            {
                path_executable_ = path_ / executable;
                break;
            }
        }
        if (path_executable_.empty())
            throw FileNotFoundException("arma3.exe");
        path_workshop_target_ = target_workshop_path;
    }

    void Client::CreateArmaCfg(vector<path> const &mod_paths, path cfg_path)
    {
        if (cfg_path.empty())
            cfg_path = GetCfgPath();
        if (!fs::Exists(cfg_path))
        {
            if (!fs::Exists(cfg_path.parent_path()))
                fs::CreateDirectories(cfg_path.parent_path());
            StdUtils::CreateFile(cfg_path);
        }
        std::string existing_config = FileReadAllText(cfg_path);

        CppFilter cpp_filter{existing_config};
        auto stripped_config = cpp_filter.RemoveClass("class ModLauncherList");
        stripped_config += R"cpp(class ModLauncherList
{
)cpp";

        int mod_number = 1;
        for (auto const& mod_path : mod_paths)
            stripped_config += GenerateCfgCppForMod(mod_path, mod_number++);

        stripped_config += "};\n";

        FileWriteAllText(cfg_path, stripped_config);
    }

    bool Client::IsProton()
    {
        return path_executable_.filename() == "arma3_x64.exe";
    }

    void Client::Start(string const &arguments, string const& user_environment_variables, bool launch_directly, bool disable_esync)
    {
        #ifdef __linux
        if (launch_directly)
            direct_launch(GetPath(), GetPathExecutable(), arguments, IsProton(), user_environment_variables, disable_esync);
        else
            indirect_launch_through_steam(arguments, IsProton(), user_environment_variables, disable_esync, IsFlatpak());
        #else
        (void)disable_esync; // esync does not apply on Mac OS X
        if (launch_directly)
        {
            try
            {
                SteamUtils steam_utils;
                auto ld_preload_path = fmt::format("{}/Steam.AppBundle/Steam/Contents/MacOS/gameoverlayrenderer.dylib", steam_utils.GetSteamPath().string());
                if (char const *old_ld_preload = getenv("DYLD_INSERT_LIBRARIES"); old_ld_preload)
                    ld_preload_path += fmt::format("{}:{}", ld_preload_path, old_ld_preload);
                auto const environment = fmt::format(R"env(DYLD_INSERT_LIBRARIES="{}")env", ld_preload_path);
                auto const command = fmt::format(R"command(env {} {} "{}/Contents/MacOS/ArmA3" {})command", environment, user_environment_variables, GetPathExecutable().string(), arguments);
                fmt::print("Running Arma:\n{}\n", command);
                StdUtils::StartBackgroundProcess(command, GetPath().string());
            }
            catch (std::exception const& e)
            {
                fmt::print(stderr, "Direct launch failed, exception: {}\n", e.what());
            }
        }
        else
        {
            std::string launch_command = fmt::format("env {} open steam://run/107410//" + StringUtils::Replace(arguments, " ", "%20"), user_environment_variables);
            StdUtils::StartBackgroundProcess(launch_command);
        }
        #endif
    }

    std::vector<Mod> Client::GetHomeMods()
    {
        return GetModsFromDirectory(GetPath());
    }

    std::vector<Mod> Client::GetWorkshopMods()
    {
        return GetModsFromDirectory(GetPathWorkshop());
    }

    std::filesystem::path Client::GetCfgPath()
    {
        if (IsProton())
            return GetPath() / Definitions::proton_config_relative_path;
        else if (IsFlatpak())
            return std::filesystem::path(Definitions::home_directory)
                   / Definitions::flatpak_prefix
                   / Definitions::local_share_prefix
                   / Definitions::bohemia_interactive_prefix
                   / Definitions::game_config_path;
        else
            return std::filesystem::path(Definitions::home_directory)
                   / Definitions::local_share_prefix
                   / Definitions::bohemia_interactive_prefix
                   / Definitions::game_config_path;
    }

    char Client::GetFakeDriveLetter()
    {
        return IsProton() ? 'Z' : 'C';
    }

    string Client::GenerateCfgCppForMod(path const &mod_path, int mod_index)
    {
        constexpr char const *mod_template =
            R"cpp(    class Mod{}
    {{
        dir="{}";
        name="{}";
        origin="GAME DIR";
        fullPath="{}";
    }};
)cpp";

        Mod mod(mod_path);
        path mod_path_absolute = trim(mod.path_.string(), "\"");
        path final_path = StringUtils::ToWindowsPath(mod_path_absolute, GetFakeDriveLetter());
        path dir = trim(mod_path_absolute.filename().string(), "\"");
        auto name = mod.GetValueOrReturnDefault(dir, "name", "dir", "tooltip", "name_read_failed");
        name = StringUtils::Replace(name, "\"", "_");

        return fmt::format(mod_template, mod_index, dir.string(), name, final_path.string());
    }

    bool Client::IsFlatpak()
    {
#ifdef __linux
        try
        {
            SteamUtils steam_utils;
            return steam_utils.IsFlatpak();
        }
        catch (std::exception const &e)
        {
            fmt::print(stderr, "Exception trying to determine if Flatpak, exception text: {}\n", e.what());
            return false;
        }
#else
        return false;
#endif
    }

    std::filesystem::path const &Client::GetPath()
    {
        return path_;
    }

    std::filesystem::path const &Client::GetPathExecutable()
    {
        return path_executable_;
    }

    std::filesystem::path const &Client::GetPathWorkshop()
    {
        return path_workshop_target_;
    }

    std::vector<Mod> Client::GetModsFromDirectory(path const &dir)
    {
        std::vector<Mod> ret;

        for (auto const &ent : fs::Ls(dir))
        {
            if (StdUtils::Contains(Definitions::exclusions, ent))
                continue;

            std::filesystem::path mod_dir = dir / ent;
            if (!fs::IsDirectory(mod_dir))
                continue;
            if (!StdUtils::Contains(fs::Ls(mod_dir, true), "addons"))
                continue;

            ret.emplace_back(mod_dir);
        }

        std::sort(ret.begin(), ret.end(), [](Mod const & m1, Mod const & m2)
        {
            return m1.GetValueOrReturnDefault("name", "dir", StringUtils::Lowercase(m1.path_)) < m2.GetValueOrReturnDefault("name", "dir", StringUtils::Lowercase(m2.path_));
        });

        return ret;
    }
}
<commit_msg>arma3client: print config before writing it to disk<commit_after>#include "arma3client.hpp"

#include <algorithm>
#include <filesystem>
#include <string>

#include <fmt/format.h>
#include <fmt/ostream.h>

#include <static_todo.hpp>

#include "cppfilter.hpp"
#include "filesystem_utils.hpp"
#include "steam_utils.hpp"
#include "std_utils.hpp"
#include "string_utils.hpp"

#include "exceptions/directory_not_found.hpp"
#include "exceptions/file_no_access.hpp"
#include "exceptions/file_not_found.hpp"
#include "exceptions/not_a_symlink.hpp"
#include "exceptions/syntax_error.hpp"

using namespace std;
using namespace std::filesystem;

using namespace StdUtils;
using namespace StringUtils;

namespace fs = FilesystemUtils;

#ifdef __linux
namespace
{
    std::string get_esync_prefix(bool disable_esync)
    {
        if (disable_esync)
            return "PROTON_NO_ESYNC=1";
        return "";
    }

    std::string optional_steam_runtime(SteamUtils const &steam_utils)
    {
        if (StdUtils::IsLibraryAvailable("libpng12.so"))
            return "";
        auto const steam_runtime_path = steam_utils.GetSteamPath() / "ubuntu12_32/steam-runtime/run.sh";
        if (fs::Exists(steam_runtime_path))
            return steam_runtime_path;
        fmt::print(stderr, "Did not find {} and libpng12.so is missing. Thermal optics will be broken!\n", steam_runtime_path);
        return "";
    }

    std::string get_verb(std::string const &arguments)
    {
        return StringUtils::Replace(arguments, "%verb%", "run");;
    }

    void direct_launch(std::filesystem::path const &arma_path, std::filesystem::path const &executable_path,
                       string const &arguments, bool is_proton, string const &user_environment_variables, bool disable_esync)
    {
        if (!is_proton)
        {
            StdUtils::StartBackgroundProcess(fmt::format("env {} {} {}", user_environment_variables, executable_path, arguments),
                                             arma_path.string());
            return;
        }

        try
        {
            SteamUtils steam_utils;
            auto const arma3_id = std::stoull(ARMA3::Definitions::app_id);
            auto const compatibility_tool = steam_utils.GetCompatibilityToolForAppId(arma3_id);

            auto ld_preload_path = fmt::format("{}/ubuntu12_64/gameoverlayrenderer.so", steam_utils.GetSteamPath().string());
            if (char const *old_ld_preload = getenv("LD_PRELOAD"); old_ld_preload)
                ld_preload_path = fmt::format("{}:{}", ld_preload_path, old_ld_preload);

            auto const steam_compat_data_path = steam_utils.GetInstallPathFromGamePath(arma_path) / "steamapps/compatdata" /
                                                ARMA3::Definitions::app_id;

            auto const environment = fmt::format(R"env({} SteamGameId={} LD_PRELOAD={} STEAM_COMPAT_DATA_PATH="{}")env",
                                                 get_esync_prefix(disable_esync), arma3_id, ld_preload_path, steam_compat_data_path.string());
            auto const command = fmt::format(R"command(env {} {} {} {} {} "{}" {})command", environment, user_environment_variables,
                                             optional_steam_runtime(steam_utils), compatibility_tool.first,
                                             get_verb(compatibility_tool.second), executable_path.string(), arguments);
            fmt::print("Running Arma:\n{}\n", command);
            StdUtils::StartBackgroundProcess(command, arma_path.string());
        }
        catch (std::exception const &e)
        {
            fmt::print(stderr, "Direct launch failed, exception: {}\n", e.what());
        }
    }

    void indirect_flatpak_launch(string const &arguments, bool is_proton, string const &user_environment_variables,
                                 bool disable_esync)
    {
        if (is_proton)
            StdUtils::StartBackgroundProcess(
                fmt::format("flatpak run --env=\"{} {}\" com.valvesoftware.Steam -applaunch 107410 -nolauncher {}",
                            get_esync_prefix(disable_esync), user_environment_variables, arguments));
        else
            StdUtils::StartBackgroundProcess(fmt::format("flatpak run com.valvesoftware.Steam -applaunch 107410 -nolauncher {}",
                                             arguments));
    }

    void indirect_launch(string const &arguments, bool is_proton, string const &user_environment_variables,
                         bool disable_esync)
    {
        if (is_proton)
            StdUtils::StartBackgroundProcess(fmt::format("env {} {} steam -applaunch 107410 -nolauncher {}",
                                             get_esync_prefix(disable_esync), user_environment_variables, arguments));
        else
            StdUtils::StartBackgroundProcess(fmt::format("env {} steam -applaunch 107410 {}", user_environment_variables,
                                             arguments));
    }

    void indirect_launch_through_steam(string const &arguments, bool is_proton, string const &user_environment_variables,
                                       bool disable_esync, bool is_flatpak)
    {
        if (is_flatpak)
            indirect_flatpak_launch(arguments, is_proton, user_environment_variables, disable_esync);
        else
            indirect_launch(arguments, is_proton, user_environment_variables, disable_esync);
    }
}
#endif

namespace ARMA3
{
    Client::Client(std::filesystem::path const &arma_path, std::filesystem::path const &target_workshop_path)
    {
        path_ = arma_path;
        for (auto const &executable : Definitions::executable_names)
        {
            if (fs::Exists(path_ / executable))
            {
                path_executable_ = path_ / executable;
                break;
            }
        }
        if (path_executable_.empty())
            throw FileNotFoundException("arma3.exe");
        path_workshop_target_ = target_workshop_path;
    }

    void Client::CreateArmaCfg(vector<path> const &mod_paths, path cfg_path)
    {
        if (cfg_path.empty())
            cfg_path = GetCfgPath();
        if (!fs::Exists(cfg_path))
        {
            if (!fs::Exists(cfg_path.parent_path()))
                fs::CreateDirectories(cfg_path.parent_path());
            StdUtils::CreateFile(cfg_path);
        }
        std::string existing_config = FileReadAllText(cfg_path);

        CppFilter cpp_filter{existing_config};
        auto stripped_config = cpp_filter.RemoveClass("class ModLauncherList");
        stripped_config += R"cpp(class ModLauncherList
{
)cpp";

        int mod_number = 1;
        for (auto const& mod_path : mod_paths)
            stripped_config += GenerateCfgCppForMod(mod_path, mod_number++);

        stripped_config += "};\n";

        fmt::print(stderr, "Writing config to '{}':\n{}\n", cfg_path.string(), stripped_config);

        FileWriteAllText(cfg_path, stripped_config);
    }

    bool Client::IsProton()
    {
        return path_executable_.filename() == "arma3_x64.exe";
    }

    void Client::Start(string const &arguments, string const& user_environment_variables, bool launch_directly, bool disable_esync)
    {
        #ifdef __linux
        if (launch_directly)
            direct_launch(GetPath(), GetPathExecutable(), arguments, IsProton(), user_environment_variables, disable_esync);
        else
            indirect_launch_through_steam(arguments, IsProton(), user_environment_variables, disable_esync, IsFlatpak());
        #else
        (void)disable_esync; // esync does not apply on Mac OS X
        if (launch_directly)
        {
            try
            {
                SteamUtils steam_utils;
                auto ld_preload_path = fmt::format("{}/Steam.AppBundle/Steam/Contents/MacOS/gameoverlayrenderer.dylib", steam_utils.GetSteamPath().string());
                if (char const *old_ld_preload = getenv("DYLD_INSERT_LIBRARIES"); old_ld_preload)
                    ld_preload_path += fmt::format("{}:{}", ld_preload_path, old_ld_preload);
                auto const environment = fmt::format(R"env(DYLD_INSERT_LIBRARIES="{}")env", ld_preload_path);
                auto const command = fmt::format(R"command(env {} {} "{}/Contents/MacOS/ArmA3" {})command", environment, user_environment_variables, GetPathExecutable().string(), arguments);
                fmt::print("Running Arma:\n{}\n", command);
                StdUtils::StartBackgroundProcess(command, GetPath().string());
            }
            catch (std::exception const& e)
            {
                fmt::print(stderr, "Direct launch failed, exception: {}\n", e.what());
            }
        }
        else
        {
            std::string launch_command = fmt::format("env {} open steam://run/107410//" + StringUtils::Replace(arguments, " ", "%20"), user_environment_variables);
            StdUtils::StartBackgroundProcess(launch_command);
        }
        #endif
    }

    std::vector<Mod> Client::GetHomeMods()
    {
        return GetModsFromDirectory(GetPath());
    }

    std::vector<Mod> Client::GetWorkshopMods()
    {
        return GetModsFromDirectory(GetPathWorkshop());
    }

    std::filesystem::path Client::GetCfgPath()
    {
        if (IsProton())
            return GetPath() / Definitions::proton_config_relative_path;
        else if (IsFlatpak())
            return std::filesystem::path(Definitions::home_directory)
                   / Definitions::flatpak_prefix
                   / Definitions::local_share_prefix
                   / Definitions::bohemia_interactive_prefix
                   / Definitions::game_config_path;
        else
            return std::filesystem::path(Definitions::home_directory)
                   / Definitions::local_share_prefix
                   / Definitions::bohemia_interactive_prefix
                   / Definitions::game_config_path;
    }

    char Client::GetFakeDriveLetter()
    {
        return IsProton() ? 'Z' : 'C';
    }

    string Client::GenerateCfgCppForMod(path const &mod_path, int mod_index)
    {
        constexpr char const *mod_template =
            R"cpp(    class Mod{}
    {{
        dir="{}";
        name="{}";
        origin="GAME DIR";
        fullPath="{}";
    }};
)cpp";

        Mod mod(mod_path);
        path mod_path_absolute = trim(mod.path_.string(), "\"");
        path final_path = StringUtils::ToWindowsPath(mod_path_absolute, GetFakeDriveLetter());
        path dir = trim(mod_path_absolute.filename().string(), "\"");
        auto name = mod.GetValueOrReturnDefault(dir, "name", "dir", "tooltip", "name_read_failed");
        name = StringUtils::Replace(name, "\"", "_");

        return fmt::format(mod_template, mod_index, dir.string(), name, final_path.string());
    }

    bool Client::IsFlatpak()
    {
#ifdef __linux
        try
        {
            SteamUtils steam_utils;
            return steam_utils.IsFlatpak();
        }
        catch (std::exception const &e)
        {
            fmt::print(stderr, "Exception trying to determine if Flatpak, exception text: {}\n", e.what());
            return false;
        }
#else
        return false;
#endif
    }

    std::filesystem::path const &Client::GetPath()
    {
        return path_;
    }

    std::filesystem::path const &Client::GetPathExecutable()
    {
        return path_executable_;
    }

    std::filesystem::path const &Client::GetPathWorkshop()
    {
        return path_workshop_target_;
    }

    std::vector<Mod> Client::GetModsFromDirectory(path const &dir)
    {
        std::vector<Mod> ret;

        for (auto const &ent : fs::Ls(dir))
        {
            if (StdUtils::Contains(Definitions::exclusions, ent))
                continue;

            std::filesystem::path mod_dir = dir / ent;
            if (!fs::IsDirectory(mod_dir))
                continue;
            if (!StdUtils::Contains(fs::Ls(mod_dir, true), "addons"))
                continue;

            ret.emplace_back(mod_dir);
        }

        std::sort(ret.begin(), ret.end(), [](Mod const & m1, Mod const & m2)
        {
            return m1.GetValueOrReturnDefault("name", "dir", StringUtils::Lowercase(m1.path_)) < m2.GetValueOrReturnDefault("name", "dir", StringUtils::Lowercase(m2.path_));
        });

        return ret;
    }
}
<|endoftext|>


<commit_before>// Copyright 2014 The Crashpad 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 "handler/handler_main.h"

#include <getopt.h>
#include <stdlib.h>

#include <map>
#include <string>

#include "base/auto_reset.h"
#include "base/files/file_path.h"
#include "base/files/scoped_file.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/scoped_generic.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "client/crash_report_database.h"
#include "client/crashpad_client.h"
#include "tools/tool_support.h"
#include "handler/crash_report_upload_thread.h"
#include "util/file/file_io.h"
#include "util/stdlib/move.h"
#include "util/stdlib/map_insert.h"
#include "util/stdlib/string_number_conversion.h"
#include "util/string/split_string.h"
#include "util/synchronization/semaphore.h"

#if defined(OS_MACOSX)
#include <libgen.h>
#include <signal.h>

#include "base/mac/scoped_mach_port.h"
#include "handler/mac/crash_report_exception_handler.h"
#include "handler/mac/exception_handler_server.h"
#include "util/mach/child_port_handshake.h"
#include "util/mach/mach_extensions.h"
#include "util/posix/close_stdio.h"
#elif defined(OS_WIN)
#include <windows.h>

#include "handler/win/crash_report_exception_handler.h"
#include "util/win/exception_handler_server.h"
#include "util/win/handle.h"
#endif  // OS_MACOSX

namespace crashpad {

namespace {

void Usage(const base::FilePath& me) {
  fprintf(stderr,
"Usage: %" PRFilePath " [OPTION]...\n"
"Crashpad's exception handler server.\n"
"\n"
"      --annotation=KEY=VALUE  set a process annotation in each crash report\n"
"      --database=PATH         store the crash report database at PATH\n"
#if defined(OS_MACOSX)
"      --handshake-fd=FD       establish communication with the client over FD\n"
"      --mach-service=SERVICE  register SERVICE with the bootstrap server\n"
"      --reset-own-crash-exception-port-to-system-default\n"
"                              reset the server's exception handler to default\n"
#elif defined(OS_WIN)
"      --handshake-handle=HANDLE\n"
"                              create a new pipe and send its name via HANDLE\n"
"      --pipe-name=PIPE        communicate with the client over PIPE\n"
#endif  // OS_MACOSX
"      --url=URL               send crash reports to this Breakpad server URL,\n"
"                              only if uploads are enabled for the database\n"
"      --help                  display this help and exit\n"
"      --version               output version information and exit\n",
          me.value().c_str());
  ToolSupport::UsageTail(me);
}

#if defined(OS_MACOSX)

struct ResetSIGTERMTraits {
  static struct sigaction* InvalidValue() {
    return nullptr;
  }

  static void Free(struct sigaction* sa) {
    int rv = sigaction(SIGTERM, sa, nullptr);
    PLOG_IF(ERROR, rv != 0) << "sigaction";
  }
};
using ScopedResetSIGTERM =
    base::ScopedGeneric<struct sigaction*, ResetSIGTERMTraits>;

ExceptionHandlerServer* g_exception_handler_server;

// This signal handler is only operative when being run from launchd.
void HandleSIGTERM(int sig, siginfo_t* siginfo, void* context) {
  DCHECK(g_exception_handler_server);
  g_exception_handler_server->Stop();
}

#endif  // OS_MACOSX

}  // namespace

int HandlerMain(int argc, char* argv[]) {
  const base::FilePath argv0(
      ToolSupport::CommandLineArgumentToFilePathStringType(argv[0]));
  const base::FilePath me(argv0.BaseName());

  enum OptionFlags {
    // Long options without short equivalents.
    kOptionLastChar = 255,
    kOptionAnnotation,
    kOptionDatabase,
#if defined(OS_MACOSX)
    kOptionHandshakeFD,
    kOptionMachService,
    kOptionResetOwnCrashExceptionPortToSystemDefault,
#elif defined(OS_WIN)
    kOptionHandshakeHandle,
    kOptionPipeName,
#endif  // OS_MACOSX
    kOptionURL,

    // Standard options.
    kOptionHelp = -2,
    kOptionVersion = -3,
  };

  struct {
    std::map<std::string, std::string> annotations;
    std::string url;
    const char* database;
#if defined(OS_MACOSX)
    int handshake_fd;
    std::string mach_service;
    bool reset_own_crash_exception_port_to_system_default;
#elif defined(OS_WIN)
    HANDLE handshake_handle;
    std::string pipe_name;
#endif  // OS_MACOSX
  } options = {};
#if defined(OS_MACOSX)
  options.handshake_fd = -1;
#elif defined(OS_WIN)
  options.handshake_handle = INVALID_HANDLE_VALUE;
#endif

  const option long_options[] = {
      {"annotation", required_argument, nullptr, kOptionAnnotation},
      {"database", required_argument, nullptr, kOptionDatabase},
#if defined(OS_MACOSX)
      {"handshake-fd", required_argument, nullptr, kOptionHandshakeFD},
      {"mach-service", required_argument, nullptr, kOptionMachService},
      {"reset-own-crash-exception-port-to-system-default",
       no_argument,
       nullptr,
       kOptionResetOwnCrashExceptionPortToSystemDefault},
#elif defined(OS_WIN)
      {"handshake-handle", required_argument, nullptr, kOptionHandshakeHandle},
      {"pipe-name", required_argument, nullptr, kOptionPipeName},
#endif  // OS_MACOSX
      {"url", required_argument, nullptr, kOptionURL},
      {"help", no_argument, nullptr, kOptionHelp},
      {"version", no_argument, nullptr, kOptionVersion},
      {nullptr, 0, nullptr, 0},
  };

  int opt;
  while ((opt = getopt_long(argc, argv, "", long_options, nullptr)) != -1) {
    switch (opt) {
      case kOptionAnnotation: {
        std::string key;
        std::string value;
        if (!SplitString(optarg, '=', &key, &value)) {
          ToolSupport::UsageHint(me, "--annotation requires KEY=VALUE");
          return EXIT_FAILURE;
        }
        std::string old_value;
        if (!MapInsertOrReplace(&options.annotations, key, value, &old_value)) {
          LOG(WARNING) << "duplicate key " << key << ", discarding value "
                       << old_value;
        }
        break;
      }
      case kOptionDatabase: {
        options.database = optarg;
        break;
      }
#if defined(OS_MACOSX)
      case kOptionHandshakeFD: {
        if (!StringToNumber(optarg, &options.handshake_fd) ||
            options.handshake_fd < 0) {
          ToolSupport::UsageHint(me,
                                 "--handshake-fd requires a file descriptor");
          return EXIT_FAILURE;
        }
        break;
      }
      case kOptionMachService: {
        options.mach_service = optarg;
        break;
      }
      case kOptionResetOwnCrashExceptionPortToSystemDefault: {
        options.reset_own_crash_exception_port_to_system_default = true;
        break;
      }
#elif defined(OS_WIN)
      case kOptionHandshakeHandle: {
        // Use unsigned int, because the handle was presented by the client in
        // 0x%x format.
        unsigned int handle_uint;
        if (!StringToNumber(optarg, &handle_uint) ||
            (options.handshake_handle = IntToHandle(handle_uint)) ==
                INVALID_HANDLE_VALUE) {
          ToolSupport::UsageHint(me, "--handshake-handle requires a HANDLE");
          return EXIT_FAILURE;
        }
        break;
      }
      case kOptionPipeName: {
        options.pipe_name = optarg;
        break;
      }
#endif  // OS_MACOSX
      case kOptionURL: {
        options.url = optarg;
        break;
      }
      case kOptionHelp: {
        Usage(me);
        return EXIT_SUCCESS;
      }
      case kOptionVersion: {
        ToolSupport::Version(me);
        return EXIT_SUCCESS;
      }
      default: {
        ToolSupport::UsageHint(me, nullptr);
        return EXIT_FAILURE;
      }
    }
  }
  argc -= optind;
  argv += optind;

#if defined(OS_MACOSX)
  if (options.handshake_fd < 0 && options.mach_service.empty()) {
    ToolSupport::UsageHint(me, "--handshake-fd or --mach-service is required");
    return EXIT_FAILURE;
  }
  if (options.handshake_fd >= 0 && !options.mach_service.empty()) {
    ToolSupport::UsageHint(
        me, "--handshake-fd and --mach-service are incompatible");
    return EXIT_FAILURE;
  }
#elif defined(OS_WIN)
  if (options.handshake_handle == INVALID_HANDLE_VALUE &&
      options.pipe_name.empty()) {
    ToolSupport::UsageHint(me, "--handshake-handle or --pipe-name is required");
    return EXIT_FAILURE;
  }
  if (options.handshake_handle != INVALID_HANDLE_VALUE &&
      !options.pipe_name.empty()) {
    ToolSupport::UsageHint(
        me, "--handshake-handle and --pipe-name are incompatible");
    return EXIT_FAILURE;
  }
#endif  // OS_MACOSX

  if (!options.database) {
    ToolSupport::UsageHint(me, "--database is required");
    return EXIT_FAILURE;
  }

  if (argc) {
    ToolSupport::UsageHint(me, nullptr);
    return EXIT_FAILURE;
  }

#if defined(OS_MACOSX)
  if (options.mach_service.empty()) {
    // Don’t do this when being run by launchd. See launchd.plist(5).
    CloseStdinAndStdout();
  }

  if (options.reset_own_crash_exception_port_to_system_default) {
    CrashpadClient::UseSystemDefaultHandler();
  }

  base::mac::ScopedMachReceiveRight receive_right;

  if (options.handshake_fd >= 0) {
    receive_right.reset(
        ChildPortHandshake::RunServerForFD(
            base::ScopedFD(options.handshake_fd),
            ChildPortHandshake::PortRightType::kReceiveRight));
  } else if (!options.mach_service.empty()) {
    receive_right = BootstrapCheckIn(options.mach_service);
  }

  if (!receive_right.is_valid()) {
    return EXIT_FAILURE;
  }

  ExceptionHandlerServer exception_handler_server(
      crashpad::move(receive_right), !options.mach_service.empty());
  base::AutoReset<ExceptionHandlerServer*> reset_g_exception_handler_server(
      &g_exception_handler_server, &exception_handler_server);

  struct sigaction old_sa;
  ScopedResetSIGTERM reset_sigterm;
  if (!options.mach_service.empty()) {
    // When running from launchd, no no-senders notification could ever be
    // triggered, because launchd maintains a send right to the service. When
    // launchd wants the job to exit, it will send a SIGTERM. See
    // launchd.plist(5).
    //
    // Set up a SIGTERM handler that will call exception_handler_server.Stop().
    struct sigaction sa = {};
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_SIGINFO;
    sa.sa_sigaction = HandleSIGTERM;
    int rv = sigaction(SIGTERM, &sa, &old_sa);
    PCHECK(rv == 0) << "sigaction";
    reset_sigterm.reset(&old_sa);
  }
#elif defined(OS_WIN)
  ExceptionHandlerServer exception_handler_server(!options.pipe_name.empty());

  if (!options.pipe_name.empty()) {
    exception_handler_server.SetPipeName(base::UTF8ToUTF16(options.pipe_name));
  } else if (options.handshake_handle != INVALID_HANDLE_VALUE) {
    std::wstring pipe_name = exception_handler_server.CreatePipe();

    uint32_t pipe_name_length = static_cast<uint32_t>(pipe_name.size());
    if (!LoggingWriteFile(options.handshake_handle,
                          &pipe_name_length,
                          sizeof(pipe_name_length))) {
      return EXIT_FAILURE;
    }
    if (!LoggingWriteFile(options.handshake_handle,
                          pipe_name.c_str(),
                          pipe_name.size() * sizeof(pipe_name[0]))) {
      return EXIT_FAILURE;
    }
  }
#endif  // OS_MACOSX

  scoped_ptr<CrashReportDatabase> database(CrashReportDatabase::Initialize(
      base::FilePath(ToolSupport::CommandLineArgumentToFilePathStringType(
          options.database))));
  if (!database) {
    return EXIT_FAILURE;
  }

  CrashReportUploadThread upload_thread(database.get(), options.url);
  upload_thread.Start();

  CrashReportExceptionHandler exception_handler(
      database.get(), &upload_thread, &options.annotations);

  exception_handler_server.Run(&exception_handler);

  upload_thread.Stop();

  return EXIT_SUCCESS;
}

}  // namespace crashpad
<commit_msg>win: Set shutdown order to make the handler shutdown as late as possible<commit_after>// Copyright 2014 The Crashpad 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 "handler/handler_main.h"

#include <getopt.h>
#include <stdlib.h>

#include <map>
#include <string>

#include "base/auto_reset.h"
#include "base/files/file_path.h"
#include "base/files/scoped_file.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/scoped_generic.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "client/crash_report_database.h"
#include "client/crashpad_client.h"
#include "tools/tool_support.h"
#include "handler/crash_report_upload_thread.h"
#include "util/file/file_io.h"
#include "util/stdlib/move.h"
#include "util/stdlib/map_insert.h"
#include "util/stdlib/string_number_conversion.h"
#include "util/string/split_string.h"
#include "util/synchronization/semaphore.h"

#if defined(OS_MACOSX)
#include <libgen.h>
#include <signal.h>

#include "base/mac/scoped_mach_port.h"
#include "handler/mac/crash_report_exception_handler.h"
#include "handler/mac/exception_handler_server.h"
#include "util/mach/child_port_handshake.h"
#include "util/mach/mach_extensions.h"
#include "util/posix/close_stdio.h"
#elif defined(OS_WIN)
#include <windows.h>

#include "handler/win/crash_report_exception_handler.h"
#include "util/win/exception_handler_server.h"
#include "util/win/handle.h"
#endif  // OS_MACOSX

namespace crashpad {

namespace {

void Usage(const base::FilePath& me) {
  fprintf(stderr,
"Usage: %" PRFilePath " [OPTION]...\n"
"Crashpad's exception handler server.\n"
"\n"
"      --annotation=KEY=VALUE  set a process annotation in each crash report\n"
"      --database=PATH         store the crash report database at PATH\n"
#if defined(OS_MACOSX)
"      --handshake-fd=FD       establish communication with the client over FD\n"
"      --mach-service=SERVICE  register SERVICE with the bootstrap server\n"
"      --reset-own-crash-exception-port-to-system-default\n"
"                              reset the server's exception handler to default\n"
#elif defined(OS_WIN)
"      --handshake-handle=HANDLE\n"
"                              create a new pipe and send its name via HANDLE\n"
"      --pipe-name=PIPE        communicate with the client over PIPE\n"
#endif  // OS_MACOSX
"      --url=URL               send crash reports to this Breakpad server URL,\n"
"                              only if uploads are enabled for the database\n"
"      --help                  display this help and exit\n"
"      --version               output version information and exit\n",
          me.value().c_str());
  ToolSupport::UsageTail(me);
}

#if defined(OS_MACOSX)

struct ResetSIGTERMTraits {
  static struct sigaction* InvalidValue() {
    return nullptr;
  }

  static void Free(struct sigaction* sa) {
    int rv = sigaction(SIGTERM, sa, nullptr);
    PLOG_IF(ERROR, rv != 0) << "sigaction";
  }
};
using ScopedResetSIGTERM =
    base::ScopedGeneric<struct sigaction*, ResetSIGTERMTraits>;

ExceptionHandlerServer* g_exception_handler_server;

// This signal handler is only operative when being run from launchd.
void HandleSIGTERM(int sig, siginfo_t* siginfo, void* context) {
  DCHECK(g_exception_handler_server);
  g_exception_handler_server->Stop();
}

#endif  // OS_MACOSX

}  // namespace

int HandlerMain(int argc, char* argv[]) {
  const base::FilePath argv0(
      ToolSupport::CommandLineArgumentToFilePathStringType(argv[0]));
  const base::FilePath me(argv0.BaseName());

  enum OptionFlags {
    // Long options without short equivalents.
    kOptionLastChar = 255,
    kOptionAnnotation,
    kOptionDatabase,
#if defined(OS_MACOSX)
    kOptionHandshakeFD,
    kOptionMachService,
    kOptionResetOwnCrashExceptionPortToSystemDefault,
#elif defined(OS_WIN)
    kOptionHandshakeHandle,
    kOptionPipeName,
#endif  // OS_MACOSX
    kOptionURL,

    // Standard options.
    kOptionHelp = -2,
    kOptionVersion = -3,
  };

  struct {
    std::map<std::string, std::string> annotations;
    std::string url;
    const char* database;
#if defined(OS_MACOSX)
    int handshake_fd;
    std::string mach_service;
    bool reset_own_crash_exception_port_to_system_default;
#elif defined(OS_WIN)
    HANDLE handshake_handle;
    std::string pipe_name;
#endif  // OS_MACOSX
  } options = {};
#if defined(OS_MACOSX)
  options.handshake_fd = -1;
#elif defined(OS_WIN)
  options.handshake_handle = INVALID_HANDLE_VALUE;
#endif

  const option long_options[] = {
      {"annotation", required_argument, nullptr, kOptionAnnotation},
      {"database", required_argument, nullptr, kOptionDatabase},
#if defined(OS_MACOSX)
      {"handshake-fd", required_argument, nullptr, kOptionHandshakeFD},
      {"mach-service", required_argument, nullptr, kOptionMachService},
      {"reset-own-crash-exception-port-to-system-default",
       no_argument,
       nullptr,
       kOptionResetOwnCrashExceptionPortToSystemDefault},
#elif defined(OS_WIN)
      {"handshake-handle", required_argument, nullptr, kOptionHandshakeHandle},
      {"pipe-name", required_argument, nullptr, kOptionPipeName},
#endif  // OS_MACOSX
      {"url", required_argument, nullptr, kOptionURL},
      {"help", no_argument, nullptr, kOptionHelp},
      {"version", no_argument, nullptr, kOptionVersion},
      {nullptr, 0, nullptr, 0},
  };

  int opt;
  while ((opt = getopt_long(argc, argv, "", long_options, nullptr)) != -1) {
    switch (opt) {
      case kOptionAnnotation: {
        std::string key;
        std::string value;
        if (!SplitString(optarg, '=', &key, &value)) {
          ToolSupport::UsageHint(me, "--annotation requires KEY=VALUE");
          return EXIT_FAILURE;
        }
        std::string old_value;
        if (!MapInsertOrReplace(&options.annotations, key, value, &old_value)) {
          LOG(WARNING) << "duplicate key " << key << ", discarding value "
                       << old_value;
        }
        break;
      }
      case kOptionDatabase: {
        options.database = optarg;
        break;
      }
#if defined(OS_MACOSX)
      case kOptionHandshakeFD: {
        if (!StringToNumber(optarg, &options.handshake_fd) ||
            options.handshake_fd < 0) {
          ToolSupport::UsageHint(me,
                                 "--handshake-fd requires a file descriptor");
          return EXIT_FAILURE;
        }
        break;
      }
      case kOptionMachService: {
        options.mach_service = optarg;
        break;
      }
      case kOptionResetOwnCrashExceptionPortToSystemDefault: {
        options.reset_own_crash_exception_port_to_system_default = true;
        break;
      }
#elif defined(OS_WIN)
      case kOptionHandshakeHandle: {
        // Use unsigned int, because the handle was presented by the client in
        // 0x%x format.
        unsigned int handle_uint;
        if (!StringToNumber(optarg, &handle_uint) ||
            (options.handshake_handle = IntToHandle(handle_uint)) ==
                INVALID_HANDLE_VALUE) {
          ToolSupport::UsageHint(me, "--handshake-handle requires a HANDLE");
          return EXIT_FAILURE;
        }
        break;
      }
      case kOptionPipeName: {
        options.pipe_name = optarg;
        break;
      }
#endif  // OS_MACOSX
      case kOptionURL: {
        options.url = optarg;
        break;
      }
      case kOptionHelp: {
        Usage(me);
        return EXIT_SUCCESS;
      }
      case kOptionVersion: {
        ToolSupport::Version(me);
        return EXIT_SUCCESS;
      }
      default: {
        ToolSupport::UsageHint(me, nullptr);
        return EXIT_FAILURE;
      }
    }
  }
  argc -= optind;
  argv += optind;

#if defined(OS_MACOSX)
  if (options.handshake_fd < 0 && options.mach_service.empty()) {
    ToolSupport::UsageHint(me, "--handshake-fd or --mach-service is required");
    return EXIT_FAILURE;
  }
  if (options.handshake_fd >= 0 && !options.mach_service.empty()) {
    ToolSupport::UsageHint(
        me, "--handshake-fd and --mach-service are incompatible");
    return EXIT_FAILURE;
  }
#elif defined(OS_WIN)
  if (options.handshake_handle == INVALID_HANDLE_VALUE &&
      options.pipe_name.empty()) {
    ToolSupport::UsageHint(me, "--handshake-handle or --pipe-name is required");
    return EXIT_FAILURE;
  }
  if (options.handshake_handle != INVALID_HANDLE_VALUE &&
      !options.pipe_name.empty()) {
    ToolSupport::UsageHint(
        me, "--handshake-handle and --pipe-name are incompatible");
    return EXIT_FAILURE;
  }
#endif  // OS_MACOSX

  if (!options.database) {
    ToolSupport::UsageHint(me, "--database is required");
    return EXIT_FAILURE;
  }

  if (argc) {
    ToolSupport::UsageHint(me, nullptr);
    return EXIT_FAILURE;
  }

#if defined(OS_MACOSX)
  if (options.mach_service.empty()) {
    // Don’t do this when being run by launchd. See launchd.plist(5).
    CloseStdinAndStdout();
  }

  if (options.reset_own_crash_exception_port_to_system_default) {
    CrashpadClient::UseSystemDefaultHandler();
  }

  base::mac::ScopedMachReceiveRight receive_right;

  if (options.handshake_fd >= 0) {
    receive_right.reset(
        ChildPortHandshake::RunServerForFD(
            base::ScopedFD(options.handshake_fd),
            ChildPortHandshake::PortRightType::kReceiveRight));
  } else if (!options.mach_service.empty()) {
    receive_right = BootstrapCheckIn(options.mach_service);
  }

  if (!receive_right.is_valid()) {
    return EXIT_FAILURE;
  }

  ExceptionHandlerServer exception_handler_server(
      crashpad::move(receive_right), !options.mach_service.empty());
  base::AutoReset<ExceptionHandlerServer*> reset_g_exception_handler_server(
      &g_exception_handler_server, &exception_handler_server);

  struct sigaction old_sa;
  ScopedResetSIGTERM reset_sigterm;
  if (!options.mach_service.empty()) {
    // When running from launchd, no no-senders notification could ever be
    // triggered, because launchd maintains a send right to the service. When
    // launchd wants the job to exit, it will send a SIGTERM. See
    // launchd.plist(5).
    //
    // Set up a SIGTERM handler that will call exception_handler_server.Stop().
    struct sigaction sa = {};
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_SIGINFO;
    sa.sa_sigaction = HandleSIGTERM;
    int rv = sigaction(SIGTERM, &sa, &old_sa);
    PCHECK(rv == 0) << "sigaction";
    reset_sigterm.reset(&old_sa);
  }
#elif defined(OS_WIN)
  // Shut down as late as possible relative to programs we're watching.
  if (!SetProcessShutdownParameters(0x100, SHUTDOWN_NORETRY))
    PLOG(ERROR) << "SetProcessShutdownParameters";

  ExceptionHandlerServer exception_handler_server(!options.pipe_name.empty());

  if (!options.pipe_name.empty()) {
    exception_handler_server.SetPipeName(base::UTF8ToUTF16(options.pipe_name));
  } else if (options.handshake_handle != INVALID_HANDLE_VALUE) {
    std::wstring pipe_name = exception_handler_server.CreatePipe();

    uint32_t pipe_name_length = static_cast<uint32_t>(pipe_name.size());
    if (!LoggingWriteFile(options.handshake_handle,
                          &pipe_name_length,
                          sizeof(pipe_name_length))) {
      return EXIT_FAILURE;
    }
    if (!LoggingWriteFile(options.handshake_handle,
                          pipe_name.c_str(),
                          pipe_name.size() * sizeof(pipe_name[0]))) {
      return EXIT_FAILURE;
    }
  }
#endif  // OS_MACOSX

  scoped_ptr<CrashReportDatabase> database(CrashReportDatabase::Initialize(
      base::FilePath(ToolSupport::CommandLineArgumentToFilePathStringType(
          options.database))));
  if (!database) {
    return EXIT_FAILURE;
  }

  CrashReportUploadThread upload_thread(database.get(), options.url);
  upload_thread.Start();

  CrashReportExceptionHandler exception_handler(
      database.get(), &upload_thread, &options.annotations);

  exception_handler_server.Run(&exception_handler);

  upload_thread.Stop();

  return EXIT_SUCCESS;
}

}  // namespace crashpad
<|endoftext|>


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

/**
 * @file AtomSet.cpp
 * @author Roman Schindlauer
 * @date Tue Feb  7 17:19:18 CET 2006
 *
 * @brief AtomSet class.
 *
 *
 */

#include <vector>
#include <algorithm>

#include "dlvhex/AtomSet.h"

DLVHEX_NAMESPACE_BEGIN

void
multiplySets(std::vector<AtomSet>& s1,
			 std::vector<AtomSet>& s2,
			 std::vector<AtomSet>& result)
{
	//
	// write result into temporary vector. in case the result parameter
	// is one of the two input vectors
	//
	std::vector<AtomSet> tmpset;

	AtomSet un;

//	unsigned outer = 1;
	for (std::vector<AtomSet>::iterator i1 = s1.begin();
			i1 != s1.end();
			++i1)
	{
//		unsigned inner = 1;
//		std::cerr << "outer loop: " << outer++ << std::endl;
//		std::cerr << "outer set has: " << (*i1).size() << std::endl;
		for (std::vector<AtomSet>::iterator i2 = s2.begin();
				i2 != s2.end();
				++i2)
		{
//			std::cerr << "inner loop: " << inner++ << std::endl;
			//
			// make union of two atomsets:
			//
			un = *i1;
			un.insert(*i2);

			//
			// now ensure minimality:
			//
			bool add = true;

			std::vector<AtomSet>::iterator curras = tmpset.begin();

//			unsigned i = 1;

			if (((*i1).size() > 0) && ((*i2).size() > 0))
			while (curras != tmpset.end())
			{
//				std::cerr << "looking at existing set " << i++
//					<< "   new union: " << un.size() << " current: " << (*curras).size() << std::endl;
				//
				// is the new one a superset (or equal) than an existing one
				//
				if (std::includes(un.begin(), un.end(), (*curras).begin(), (*curras).end()))
				{
//					std::cerr << "	 new union is superset!" << std::endl;
//					std::cerr << "	 new union: " << un.size() << " current: " << (*curras).size() << std::endl;
					add = false;
					break;
				}

				//
				// is the new one a subset of an existing one? Must be a *real* subset,
				// if we passed the previous "if"!
				//
				if (std::includes((*curras).begin(), (*curras).end(), un.begin(), un.end()))
				{
//					std::cerr << "	 new union is subset!" << std::endl;
					//
					// remove existing one
					//
					tmpset.erase(curras);
					break;
				}

				curras++;
			}

			if (add)
				tmpset.push_back(un);
		}
	}

	//
	// now we can write the result
	//
	swap(result, tmpset);
}

AtomSet::const_iterator
AtomSet::begin() const
{
	return const_iterator(atoms.begin());
}

AtomSet::const_iterator
AtomSet::end() const
{
	return const_iterator(atoms.end());
}

void
AtomSet::clear()
{
	atoms.clear();
}

bool
AtomSet::empty() const
{
	return atoms.empty();
}

size_t
AtomSet::size() const
{
	return atoms.size();
}

void
AtomSet::insert(const AtomPtr& ap)
{
	/// @todo test if *ap really exists

	atoms.insert(ap);
}

void
AtomSet::insert(const AtomSet& add)
{
	atoms.insert(add.atoms.begin(), add.atoms.end());
}

AtomSet
AtomSet::difference(const AtomSet& as) const
{
  AtomSet res;

//   std::set_difference(this->atoms.begin(), this->atoms.end(),
// 		      as.atoms.begin(), as.atoms.end(),
// 		      std::inserter(res.atoms, res.atoms.begin())
// 		      );

  for (atomset_t::const_iterator a = atoms.begin();
       a != atoms.end();
       ++a)
    {
      if (as.atoms.find(*a) == as.atoms.end())
	res.atoms.insert(*a);
    }

  return res;
}

void
AtomSet::matchPredicate(const std::string& pred,
			AtomSet& matched) const
{
	/// @todo: stdlib algorithm!
	for (atomset_t::const_iterator a = atoms.begin();
		 a != atoms.end();
		 a++)
	{
		if ((*a)->getPredicate() == pred)
			matched.atoms.insert(*a);
	}
}

void
AtomSet::matchAtom(const AtomPtr& atom,
		   AtomSet& matched) const
{
	/// @todo: stdlib algorithm!
	for (atomset_t::const_iterator a = atoms.begin();
		 a != atoms.end();
		 a++)
	{
		if ((*a)->unifiesWith(atom))
			matched.atoms.insert(*a);
	}
}

void
AtomSet::accept(BaseVisitor& v)
{
  v.visit(this);
}

/**
 * @brief General purpose predicate functor, which returns true iff
 * predicate of g matches pred.
 */
struct PredicateMatches : public std::binary_function<AtomPtr, std::string, bool>
{
  bool
  operator() (const AtomPtr& g, const std::string& pred) const
  {
    return (g->getPredicate() == Term(pred));
  }
};

void
AtomSet::remove(const std::string& pred)
{
	atomset_t::iterator cur = atoms.begin();

	atomset_t::const_iterator last = atoms.end();

	while ((cur = std::find_if(cur, last, std::bind2nd(PredicateMatches(), pred))) != last)
	{
		atomset_t::iterator tmp = cur++;

		atoms.erase(tmp);
	}
}

void
AtomSet::remove(const std::vector<std::string>& preds)
{
	for (std::vector<std::string>::const_iterator predit = preds.begin();
		 predit != preds.end();
		 ++predit)
	{
		remove(*predit);
	}
}

void
AtomSet::keep(const std::vector<std::string>& preds)
{
	atomset_t::iterator cur = atoms.begin();

	atomset_t::const_iterator last = atoms.end();

	//
	// go through all atoms of this set
	//
	while (cur != last)
	{
		//
		// look if the current atom is in the filter set
		//
		if ((std::find_if(preds.begin(),
						  preds.end(),
				  std::bind1st(PredicateMatches(), *cur))) == preds.end())
		{
			//
			// if not, delete this atom
			//
			atomset_t::iterator tmp = cur++;

			atoms.erase(tmp);
		}
		else
		{
			cur++;
		}
	}
}

void
AtomSet::keepPos()
{
	atomset_t::iterator cur = atoms.begin();

	atomset_t::const_iterator last = atoms.end();

	//
	// go through all atoms of this set
	//
	while (cur != last)
	{
		if ((*cur)->isStronglyNegated())
		{
			atomset_t::iterator tmp = cur++;

			atoms.erase(tmp);
		}
		else
		{
			cur++;
		}
	}
}

/**
 * @brief General purpose predicate functor, which returns true iff
 * (*g == a).
 */
struct AtomMatches : public std::binary_function<AtomPtr, Atom, bool>
{
  bool
  operator() (const AtomPtr& g, const Atom& a) const
  {
    return (*g == a);
  }
};

bool
AtomSet::isConsistent() const
{
	atomset_t::iterator cur = atoms.begin();

	atomset_t::const_iterator last = atoms.end();

	//
	// go through all atoms of this set
	//
	while (cur != last)
	{
		Atom a(**cur);
		a.negate();

		//
		// see if 'cur' occurs negated (i.e., 'a') in the range of 'cur+1' to
		// 'last'
		//
		if (std::find_if(++cur, last, std::bind2nd(AtomMatches(), a)) != last)
			return false;
	}

	return true;
}

bool
AtomSet::operator== (const AtomSet& atomset2) const
{
	return ((this->size() == atomset2.size())
	  && std::equal(this->begin(), this->end(), atomset2.begin()));
}

bool
AtomSet::operator!= (const AtomSet& atomset2) const
{
	return !(*this == atomset2);
}

int
AtomSet::operator< (const AtomSet& atomset2) const
{
	if (this->size() < atomset2.size())
		return true;

	if (this->size() > atomset2.size())
		return false;

	//return !(std::includes(this->begin(), this->end(), atomset2.begin(), atomset2.end()));

	// find first mismatch
	std::pair<AtomSet::const_iterator, AtomSet::const_iterator> result;
	result = std::mismatch(this->begin(), this->end(), atomset2.begin());

	//
	// no mismatch? then they are equal!
	//
	if (result.first == this->end())
		return false;

	return *(result.first) < *(result.second);
}

DLVHEX_NAMESPACE_END

/* vim: set noet sw=4 ts=4 tw=80: */

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

/**
 * @file AtomSet.cpp
 * @author Roman Schindlauer
 * @date Tue Feb  7 17:19:18 CET 2006
 *
 * @brief AtomSet class.
 *
 *
 */

#include <vector>
#include <algorithm>

#include "dlvhex/AtomSet.h"

DLVHEX_NAMESPACE_BEGIN

void
multiplySets(std::vector<AtomSet>& s1,
			 std::vector<AtomSet>& s2,
			 std::vector<AtomSet>& result)
{
	//
	// write result into temporary vector. in case the result parameter
	// is one of the two input vectors
	//
	std::vector<AtomSet> tmpset;

	AtomSet un;

//	unsigned outer = 1;
	for (std::vector<AtomSet>::iterator i1 = s1.begin();
			i1 != s1.end();
			++i1)
	{
//		unsigned inner = 1;
//		std::cerr << "outer loop: " << outer++ << std::endl;
//		std::cerr << "outer set has: " << (*i1).size() << std::endl;
		for (std::vector<AtomSet>::iterator i2 = s2.begin();
				i2 != s2.end();
				++i2)
		{
//			std::cerr << "inner loop: " << inner++ << std::endl;
			//
			// make union of two atomsets:
			//
			un = *i1;
			un.insert(*i2);

			//
			// now ensure minimality:
			//
			bool add = true;

			std::vector<AtomSet>::iterator curras = tmpset.begin();

//			unsigned i = 1;

			if (((*i1).size() > 0) && ((*i2).size() > 0))
			while (curras != tmpset.end())
			{
//				std::cerr << "looking at existing set " << i++
//					<< "   new union: " << un.size() << " current: " << (*curras).size() << std::endl;
				//
				// is the new one a superset (or equal) than an existing one
				//
				if (std::includes(un.begin(), un.end(), (*curras).begin(), (*curras).end()))
				{
//					std::cerr << "	 new union is superset!" << std::endl;
//					std::cerr << "	 new union: " << un.size() << " current: " << (*curras).size() << std::endl;
					add = false;
					break;
				}

				//
				// is the new one a subset of an existing one? Must be a *real* subset,
				// if we passed the previous "if"!
				//
				if (std::includes((*curras).begin(), (*curras).end(), un.begin(), un.end()))
				{
//					std::cerr << "	 new union is subset!" << std::endl;
					//
					// remove existing one
					//
					tmpset.erase(curras);
					break;
				}

				curras++;
			}

			if (add)
				tmpset.push_back(un);
		}
	}

	//
	// now we can write the result
	//
	swap(result, tmpset);
}

AtomSet::const_iterator
AtomSet::begin() const
{
	return const_iterator(atoms.begin());
}

AtomSet::const_iterator
AtomSet::end() const
{
	return const_iterator(atoms.end());
}

void
AtomSet::clear()
{
	atoms.clear();
}

bool
AtomSet::empty() const
{
	return atoms.empty();
}

size_t
AtomSet::size() const
{
	return atoms.size();
}

void
AtomSet::insert(const AtomPtr& ap)
{
	/// @todo test if *ap really exists

	atoms.insert(ap);
}

void
AtomSet::insert(const AtomSet& add)
{
	atoms.insert(add.atoms.begin(), add.atoms.end());
}

AtomSet
AtomSet::difference(const AtomSet& as) const
{
  AtomSet res;

//   std::set_difference(this->atoms.begin(), this->atoms.end(),
// 		      as.atoms.begin(), as.atoms.end(),
// 		      std::inserter(res.atoms, res.atoms.begin())
// 		      );

  for (atomset_t::const_iterator a = atoms.begin();
       a != atoms.end();
       ++a)
    {
      if (as.atoms.find(*a) == as.atoms.end())
	res.atoms.insert(*a);
    }

  return res;
}

void
AtomSet::matchPredicate(const std::string& pred,
			AtomSet& matched) const
{
	/// @todo: stdlib algorithm!
	for (atomset_t::const_iterator a = atoms.begin();
		 a != atoms.end();
		 a++)
	{
		if ((*a)->getPredicate() == pred)
			matched.atoms.insert(*a);
	}
}

void
AtomSet::matchAtom(const AtomPtr& atom,
		   AtomSet& matched) const
{
	/// @todo: stdlib algorithm!
	for (atomset_t::const_iterator a = atoms.begin();
		 a != atoms.end();
		 a++)
	{
		if ((*a)->unifiesWith(atom))
			matched.atoms.insert(*a);
	}
}

void
AtomSet::accept(BaseVisitor& v)
{
  v.visit(this);
}

/**
 * @brief General purpose predicate functor, which returns true iff
 * predicate of g matches pred.
 */
struct PredicateMatches : public std::binary_function<AtomPtr, std::string, bool>
{
  bool
  operator() (const AtomPtr& g, const std::string& pred) const
  {
    return (g->getPredicate() == Term(pred));
  }
};

void
AtomSet::remove(const std::string& pred)
{
	atomset_t::iterator cur = atoms.begin();

	atomset_t::const_iterator last = atoms.end();

	while ((cur = std::find_if(cur, last, std::bind2nd(PredicateMatches(), pred))) != last)
	{
		atomset_t::iterator tmp = cur++;

		atoms.erase(tmp);
	}
}

void
AtomSet::remove(const std::vector<std::string>& preds)
{
	for (std::vector<std::string>::const_iterator predit = preds.begin();
		 predit != preds.end();
		 ++predit)
	{
		remove(*predit);
	}
}

void
AtomSet::keep(const std::vector<std::string>& preds)
{
	atomset_t::iterator cur = atoms.begin();

	atomset_t::const_iterator last = atoms.end();

	//
	// go through all atoms of this set
	//
	while (cur != last)
	{
		//
		// look if the current atom is in the filter set
		//
		if ((std::find_if(preds.begin(),
						  preds.end(),
				  std::bind1st(PredicateMatches(), *cur))) == preds.end())
		{
			//
			// if not, delete this atom
			//
			atomset_t::iterator tmp = cur++;

			atoms.erase(tmp);
		}
		else
		{
			cur++;
		}
	}
}

void
AtomSet::keepPos()
{
	atomset_t::iterator cur = atoms.begin();

	atomset_t::const_iterator last = atoms.end();

	//
	// go through all atoms of this set
	//
	while (cur != last)
	{
		if ((*cur)->isStronglyNegated())
		{
			atomset_t::iterator tmp = cur++;

			atoms.erase(tmp);
		}
		else
		{
			cur++;
		}
	}
}

/**
 * @brief General purpose predicate functor, which returns true iff
 * (*g == a).
 */
struct AtomMatches : public std::binary_function<AtomPtr, Atom, bool>
{
  bool
  operator() (const AtomPtr& g, const Atom& a) const
  {
    return (*g == a);
  }
};

bool
AtomSet::isConsistent() const
{
	atomset_t::iterator cur = atoms.begin();

	atomset_t::const_iterator last = atoms.end();

	//
	// go through all atoms of this set
	//
	while (cur != last)
	{
		Atom a(**cur);
		a.negate();

		//
		// see if 'cur' occurs negated (i.e., 'a') in the range of 'cur+1' to
		// 'last'
		//
		if (std::find_if(++cur, last, std::bind2nd(AtomMatches(), a)) != last)
			return false;
	}

	return true;
}

bool
AtomSet::operator== (const AtomSet& atomset2) const
{
	return ((this->size() == atomset2.size())
	  && std::equal(this->begin(), this->end(), atomset2.begin()));
}

bool
AtomSet::operator!= (const AtomSet& atomset2) const
{
	return !(*this == atomset2);
}

int
AtomSet::operator< (const AtomSet& atomset2) const
{
  if (this->size() < atomset2.size()) // <
    {
      return true;
    }
  else if (this->size() > atomset2.size()) // >
    {
      return false;
    }
  else // same size, they can still be < or >=
    {
      // find first mismatch
      std::pair<AtomSet::const_iterator, AtomSet::const_iterator> result;
      result = std::mismatch(this->begin(), this->end(), atomset2.begin());

      // no mismatch: ==, otw. check if the found mismatch is < or >=
      return result.first == this->end() ? false : *result.first < *result.second;
    }
}

DLVHEX_NAMESPACE_END

/* vim: set noet sw=4 ts=4 tw=80: */

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