<commit_before>/** \file
 *
 *  Copyright (c) 2012-2014 by Travis Gockel. All rights reserved.
 *
 *  This program is free software: you can redistribute it and/or modify it under the terms of the Apache License
 *  as published by the Apache Software Foundation, either version 2 of the License, or (at your option) any later
 *  version.
 *
 *  \author Travis Gockel (travis@gockelhut.com)
**/
#include <json-voorhees/parse.hpp>
#include <json-voorhees/array.hpp>
#include <json-voorhees/object.hpp>
#include <json-voorhees/detail/shared_buffer.hpp>

#include "char_convert.hpp"

#include <cassert>
#include <cctype>
#include <istream>
#include <set>
#include <sstream>

#include <boost/lexical_cast.hpp>

#if 0
#   include <iostream>
#   define JSONV_DBG_NEXT(x)   std::cout << x
#   define JSONV_DBG_STRUCT(x) std::cout << "\033[0;32m" << x << "\033[m"
#else
#   define JSONV_DBG_NEXT(x)
#   define JSONV_DBG_STRUCT(x)
#endif

namespace jsonv
{

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// parse_error                                                                                //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

static std::string parse_error_what(std::size_t line,
                                    std::size_t column,
                                    std::size_t character,
                                    const std::string& message
                                   )
{
    std::ostringstream stream;
    stream << "On line " << line << " column " << column << " (char " << character << "): " << message;
    return stream.str();
}

parse_error::parse_error(size_type line_, size_type column_, size_type character_, const std::string& message_) :
        std::runtime_error(parse_error_what(line_, column_, character_, message_)),
        _line(line_),
        _column(column_),
        _character(character_),
        _message(message_)
{ }

parse_error::~parse_error() throw()
{ }

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// parse                                                                                //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace detail
{

struct parse_context
{
    typedef shared_buffer::size_type size_type;

    size_type        line;
    size_type        column;
    size_type        character;
    const char*      input;
    size_type        input_size;
    char             current;
    bool             backed_off;

    explicit parse_context(const char* input, size_type length) :
            line(0),
            column(0),
            character(0),
            input(input),
            input_size(length),
            backed_off(false)
    { }

    parse_context(const parse_context&) = delete;
    parse_context& operator=(const parse_context&) = delete;

    bool next()
    {
        if (backed_off)
        {
            backed_off = false;
            return true;
        }

        if (character < input_size)
        {
            current = input[character];
            JSONV_DBG_NEXT(current);

            ++character;
            if (current == '\n' || current == '\r')
            {
                ++line;
                column = 0;
            }
            else
                ++column;

            return true;
        }
        else
            return false;
    }

    void previous()
    {
        if (backed_off)
            parse_error("Internal parser error");
        backed_off = true;
    }

    template <typename... T>
    void parse_error(T&&... message) const
    {
        std::ostringstream stream;
        parse_error_impl(stream, std::forward<T>(message)...);
    }

private:
    void parse_error_impl(std::ostringstream& stream) const
    {
        throw jsonv::parse_error(line, column, character, stream.str());
    }

    template <typename T, typename... TRest>
    void parse_error_impl(std::ostringstream& stream, T&& current, TRest&&... rest) const
    {
        stream << std::forward<T>(current);
        parse_error_impl(stream, std::forward<TRest>(rest)...);
    }
};

static bool parse_generic(parse_context& context, value& out, bool eat_whitespace = true);

static bool eat_whitespace(parse_context& context)
{
    while (context.next())
    {
        if (!isspace(context.current))
            return true;
    }
    return false;
}

static value parse_literal(parse_context& context, const value& outval, const char* const literal)
{
    assert(context.current == *literal);

    for (const char* ptr = literal; *ptr; ++ptr)
    {
        if (context.current != *ptr)
            context.parse_error("Unexpected character '", context.current, "' while trying to match ", literal);

        if (!context.next() && ptr[1])
            context.parse_error("Unexpected end while trying to match ", literal);
    }

    context.previous();
    return outval;
}

static value parse_null(parse_context& context)
{
    return parse_literal(context, value(), "null");
}

static value parse_true(parse_context& context)
{
    return parse_literal(context, true, "true");
}

static value parse_false(parse_context& context)
{
    return parse_literal(context, false, "false");
}

static std::set<char> get_allowed_number_chars()
{
    const std::string allowed_chars_src("-0123456789+-eE.");
    return std::set<char>(allowed_chars_src.begin(), allowed_chars_src.end());
}

static value parse_number(parse_context& context)
{
    // Regex is probably the more "right" way to get a number, but lexical_cast is faster and easier.
    //typedef std::regex regex;
    //static const regex number_pattern("^(-)?(0|[1-9][0-9]*)(?:(\\.)([0-9]+))?(?:([eE])([+-])?([0-9]+))?$");
                                     // 0   |1                |2   |3           |4    |5     |6
                                     // sign|whole            |dot |decimal     |ise  |esign |exp
                                     // re.compile("^(-)?(0|[1-9][0-9]*)(?:(\\.)([0-9]+))?(?:([eE])([+-])?([0-9]+))?$") \.
                                     //   .match('-123.45e+67').groups()
                                     // ('-', '123', '.', '45', 'e', '+', '67')
    static std::set<char> allowed_chars = get_allowed_number_chars();
    std::string characters(1, context.current);
    bool is_double = false;
    while (true)
    {
        if (!context.next())
            // it is okay to end the stream in the middle of a number
            break;

        if (!allowed_chars.count(context.current))
        {
            context.previous();
            break;
        }
        else
        {
            if (context.current == '.')
                is_double = true;

            characters += context.current;
        }
    }

    try
    {
        if (is_double)
            return boost::lexical_cast<double>(characters);
        else if (characters[0] == '-')
            return boost::lexical_cast<int64_t>(characters);
        else
            return static_cast<int64_t>(boost::lexical_cast<uint64_t>(characters));
    }
    catch (boost::bad_lexical_cast&)
    {
        context.parse_error("Could not extract ", is_double ? "real" : "integer", " from \"", characters, "\"");
        return value();
    }
}

static std::string parse_string(parse_context& context)
{
    assert(context.current == '\"');

    const char* characters_start = context.input + context.character;
    std::size_t characters_count = 0;

    while (true)
    {
        if (!context.next())
        {
            context.parse_error("Unterminated string \"", std::string(characters_start, characters_count));
            break;
        }

        if (context.current == '\"')
        {
            if (characters_count > 0 && characters_start[characters_count-1] == '\\'
                && !(characters_count > 1 && characters_start[characters_count-2] == '\\') //already escaped
               )
            {
                ++characters_count;
            }
            else
                break;
        }
        else
        {
            ++characters_count;
        }
    }

    try
    {
        return string_decode(characters_start, characters_count);
    }
    catch (const detail::decode_error& err)
    {
        context.parse_error("Error decoding string:", err.what());
        // return it un-decoded
        return std::string(characters_start, characters_count);
    }
}

static value parse_array(parse_context& context)
{
    assert(context.current == '[');
    JSONV_DBG_STRUCT('[');

    array arr;

    while (true)
    {
        if (!eat_whitespace(context))
        {
            context.parse_error("Unexpected end: unmatched '['");
            break;
        }

        if (context.current == ']')
        {
            break;
        }
        else if (context.current == ',')
            continue;
        else
        {
            value val;
            if (parse_generic(context, val, false))
            {
                arr.push_back(val);
            }
            else
            {
                context.parse_error("Unexpected end: unmatched '['");
                break;
            }
        }
    }
    JSONV_DBG_STRUCT(']');
    return arr;
}

static value parse_object(parse_context& context)
{
    assert(context.current == '{');
    JSONV_DBG_STRUCT('{');

    object obj;

    while (true)
    {
        if (!eat_whitespace(context))
            context.parse_error("Unexpected end: unmatched '{'");

        switch (context.current)
        {
        case ',':
            // Jump over all commas in a row.
            break;
        case '\"':
        {
            JSONV_DBG_STRUCT('(');
            std::string key = parse_string(context);
            if (!eat_whitespace(context))
                context.parse_error("Unexpected end: missing ':' for key '", key, "'");

            if (context.current != ':')
                context.parse_error("Invalid character '", context.current, "' expecting ':'");

            value val;
            if (!parse_generic(context, val))
                context.parse_error("Unexpected end: missing value for key '", key, "'");

            //object::iterator iter = obj.find(key);
            //if (iter != obj.end())
            //    context.parse_error("Duplicate key \"", key, "\"");
            obj[key] = val;
            JSONV_DBG_STRUCT(')');
            break;
        }
        case '}':
            JSONV_DBG_STRUCT('}');
            return obj;
        default:
            context.parse_error("Invalid character '", context.current, "' expecting '}' or a key (string)");
            return obj;
        }
    }
}

static bool parse_generic(parse_context& context, value& out, bool eat_whitespace_)
{
    if (eat_whitespace_)
        if (!eat_whitespace(context))
            return false;

    switch (context.current)
    {
    case '{':
        out = parse_object(context);
        return true;
    case '[':
        out = parse_array(context);
        return true;
    case '\"':
        out = parse_string(context);
        return true;
    case 'n':
        out = parse_null(context);
        return true;
    case 't':
        out = parse_true(context);
        return true;
    case 'f':
        out = parse_false(context);
        return true;
    case '-':
    case '0':
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
    case '8':
    case '9':
        out = parse_number(context);
        return true;
    default:
        context.parse_error("Invalid character '", context.current, "'");
        return true;
    }
}

}

value parse(const char* input, std::size_t length)
{
    detail::parse_context context(input, length);
    value out;
    if (detail::parse_generic(context, out))
    {
        return out;
    }
    else
    {
        context.parse_error("No input");
        return out;
    }
}

value parse(std::istream& input)
{
    // Copy the input into a buffer
    input.seekg(0, std::ios::end);
    std::size_t len = input.tellg();
    detail::shared_buffer buffer(len);
    input.seekg(0, std::ios::beg);
    std::copy(std::istreambuf_iterator<char>(input), std::istreambuf_iterator<char>(),
              buffer.get_mutable(0, len)
             );

    return parse(buffer.cbegin(), buffer.size());
}

value parse(const std::string& source)
{
    return parse(source.c_str(), source.size());
}

}
<commit_msg>Issue 28: In parse_number, do not place characters in the std::string one byte at a time.<commit_after>/** \file
 *
 *  Copyright (c) 2012-2014 by Travis Gockel. All rights reserved.
 *
 *  This program is free software: you can redistribute it and/or modify it under the terms of the Apache License
 *  as published by the Apache Software Foundation, either version 2 of the License, or (at your option) any later
 *  version.
 *
 *  \author Travis Gockel (travis@gockelhut.com)
**/
#include <json-voorhees/parse.hpp>
#include <json-voorhees/array.hpp>
#include <json-voorhees/object.hpp>
#include <json-voorhees/detail/shared_buffer.hpp>

#include "char_convert.hpp"

#include <cassert>
#include <cctype>
#include <istream>
#include <set>
#include <sstream>

#include <boost/lexical_cast.hpp>

#if 0
#   include <iostream>
#   define JSONV_DBG_NEXT(x)   std::cout << x
#   define JSONV_DBG_STRUCT(x) std::cout << "\033[0;32m" << x << "\033[m"
#else
#   define JSONV_DBG_NEXT(x)
#   define JSONV_DBG_STRUCT(x)
#endif

namespace jsonv
{

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// parse_error                                                                                //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

static std::string parse_error_what(std::size_t line,
                                    std::size_t column,
                                    std::size_t character,
                                    const std::string& message
                                   )
{
    std::ostringstream stream;
    stream << "On line " << line << " column " << column << " (char " << character << "): " << message;
    return stream.str();
}

parse_error::parse_error(size_type line_, size_type column_, size_type character_, const std::string& message_) :
        std::runtime_error(parse_error_what(line_, column_, character_, message_)),
        _line(line_),
        _column(column_),
        _character(character_),
        _message(message_)
{ }

parse_error::~parse_error() throw()
{ }

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// parse                                                                                //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace detail
{

struct parse_context
{
    typedef shared_buffer::size_type size_type;

    size_type        line;
    size_type        column;
    size_type        character;
    const char*      input;
    size_type        input_size;
    char             current;
    bool             backed_off;

    explicit parse_context(const char* input, size_type length) :
            line(0),
            column(0),
            character(0),
            input(input),
            input_size(length),
            backed_off(false)
    { }

    parse_context(const parse_context&) = delete;
    parse_context& operator=(const parse_context&) = delete;

    bool next()
    {
        if (backed_off)
        {
            backed_off = false;
            return true;
        }

        if (character < input_size)
        {
            current = input[character];
            JSONV_DBG_NEXT(current);

            ++character;
            if (current == '\n' || current == '\r')
            {
                ++line;
                column = 0;
            }
            else
                ++column;

            return true;
        }
        else
            return false;
    }

    void previous()
    {
        if (backed_off)
            parse_error("Internal parser error");
        backed_off = true;
    }

    template <typename... T>
    void parse_error(T&&... message) const
    {
        std::ostringstream stream;
        parse_error_impl(stream, std::forward<T>(message)...);
    }

private:
    void parse_error_impl(std::ostringstream& stream) const
    {
        throw jsonv::parse_error(line, column, character, stream.str());
    }

    template <typename T, typename... TRest>
    void parse_error_impl(std::ostringstream& stream, T&& current, TRest&&... rest) const
    {
        stream << std::forward<T>(current);
        parse_error_impl(stream, std::forward<TRest>(rest)...);
    }
};

static bool parse_generic(parse_context& context, value& out, bool eat_whitespace = true);

static bool eat_whitespace(parse_context& context)
{
    while (context.next())
    {
        if (!isspace(context.current))
            return true;
    }
    return false;
}

static value parse_literal(parse_context& context, const value& outval, const char* const literal)
{
    assert(context.current == *literal);

    for (const char* ptr = literal; *ptr; ++ptr)
    {
        if (context.current != *ptr)
            context.parse_error("Unexpected character '", context.current, "' while trying to match ", literal);

        if (!context.next() && ptr[1])
            context.parse_error("Unexpected end while trying to match ", literal);
    }

    context.previous();
    return outval;
}

static value parse_null(parse_context& context)
{
    return parse_literal(context, value(), "null");
}

static value parse_true(parse_context& context)
{
    return parse_literal(context, true, "true");
}

static value parse_false(parse_context& context)
{
    return parse_literal(context, false, "false");
}

static std::set<char> get_allowed_number_chars()
{
    const std::string allowed_chars_src("-0123456789+-eE.");
    return std::set<char>(allowed_chars_src.begin(), allowed_chars_src.end());
}

static value parse_number(parse_context& context)
{
    // Regex is probably the more "right" way to get a number, but lexical_cast is faster and easier.
    //typedef std::regex regex;
    //static const regex number_pattern("^(-)?(0|[1-9][0-9]*)(?:(\\.)([0-9]+))?(?:([eE])([+-])?([0-9]+))?$");
                                     // 0   |1                |2   |3           |4    |5     |6
                                     // sign|whole            |dot |decimal     |ise  |esign |exp
                                     // re.compile("^(-)?(0|[1-9][0-9]*)(?:(\\.)([0-9]+))?(?:([eE])([+-])?([0-9]+))?$") \.
                                     //   .match('-123.45e+67').groups()
                                     // ('-', '123', '.', '45', 'e', '+', '67')
    static std::set<char> allowed_chars = get_allowed_number_chars();
    const char* characters_start = context.input + context.character - 1;
    std::size_t characters_count = 1;

    bool is_double = false;
    while (true)
    {
        if (!context.next())
            // it is okay to end the stream in the middle of a number
            break;

        if (!allowed_chars.count(context.current))
        {
            context.previous();
            break;
        }
        else
        {
            if (context.current == '.')
                is_double = true;

            ++characters_count;
        }
    }

    // TODO(performance): Creating a heap-allocated string here for the sole purpose of lexical_cast and an error
    //                    message is rather pointless...
    std::string characters(characters_start, characters_count);

    try
    {

        if (is_double)
            return boost::lexical_cast<double>(characters);
        else if (characters[0] == '-')
            return boost::lexical_cast<int64_t>(characters);
        else
            return static_cast<int64_t>(boost::lexical_cast<uint64_t>(characters));
    }
    catch (boost::bad_lexical_cast&)
    {
        context.parse_error("Could not extract ", is_double ? "real" : "integer", " from \"", characters, "\"");
        return value();
    }
}

static std::string parse_string(parse_context& context)
{
    assert(context.current == '\"');

    const char* characters_start = context.input + context.character;
    std::size_t characters_count = 0;

    while (true)
    {
        if (!context.next())
        {
            context.parse_error("Unterminated string \"", std::string(characters_start, characters_count));
            break;
        }

        if (context.current == '\"')
        {
            if (characters_count > 0 && characters_start[characters_count-1] == '\\'
                && !(characters_count > 1 && characters_start[characters_count-2] == '\\') //already escaped
               )
            {
                ++characters_count;
            }
            else
                break;
        }
        else
        {
            ++characters_count;
        }
    }

    try
    {
        return string_decode(characters_start, characters_count);
    }
    catch (const detail::decode_error& err)
    {
        context.parse_error("Error decoding string:", err.what());
        // return it un-decoded
        return std::string(characters_start, characters_count);
    }
}

static value parse_array(parse_context& context)
{
    assert(context.current == '[');
    JSONV_DBG_STRUCT('[');

    array arr;

    while (true)
    {
        if (!eat_whitespace(context))
        {
            context.parse_error("Unexpected end: unmatched '['");
            break;
        }

        if (context.current == ']')
        {
            break;
        }
        else if (context.current == ',')
            continue;
        else
        {
            value val;
            if (parse_generic(context, val, false))
            {
                arr.push_back(val);
            }
            else
            {
                context.parse_error("Unexpected end: unmatched '['");
                break;
            }
        }
    }
    JSONV_DBG_STRUCT(']');
    return arr;
}

static value parse_object(parse_context& context)
{
    assert(context.current == '{');
    JSONV_DBG_STRUCT('{');

    object obj;

    while (true)
    {
        if (!eat_whitespace(context))
            context.parse_error("Unexpected end: unmatched '{'");

        switch (context.current)
        {
        case ',':
            // Jump over all commas in a row.
            break;
        case '\"':
        {
            JSONV_DBG_STRUCT('(');
            std::string key = parse_string(context);
            if (!eat_whitespace(context))
                context.parse_error("Unexpected end: missing ':' for key '", key, "'");

            if (context.current != ':')
                context.parse_error("Invalid character '", context.current, "' expecting ':'");

            value val;
            if (!parse_generic(context, val))
                context.parse_error("Unexpected end: missing value for key '", key, "'");

            //object::iterator iter = obj.find(key);
            //if (iter != obj.end())
            //    context.parse_error("Duplicate key \"", key, "\"");
            obj[key] = val;
            JSONV_DBG_STRUCT(')');
            break;
        }
        case '}':
            JSONV_DBG_STRUCT('}');
            return obj;
        default:
            context.parse_error("Invalid character '", context.current, "' expecting '}' or a key (string)");
            return obj;
        }
    }
}

static bool parse_generic(parse_context& context, value& out, bool eat_whitespace_)
{
    if (eat_whitespace_)
        if (!eat_whitespace(context))
            return false;

    switch (context.current)
    {
    case '{':
        out = parse_object(context);
        return true;
    case '[':
        out = parse_array(context);
        return true;
    case '\"':
        out = parse_string(context);
        return true;
    case 'n':
        out = parse_null(context);
        return true;
    case 't':
        out = parse_true(context);
        return true;
    case 'f':
        out = parse_false(context);
        return true;
    case '-':
    case '0':
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
    case '8':
    case '9':
        out = parse_number(context);
        return true;
    default:
        context.parse_error("Invalid character '", context.current, "'");
        return true;
    }
}

}

value parse(const char* input, std::size_t length)
{
    detail::parse_context context(input, length);
    value out;
    if (detail::parse_generic(context, out))
    {
        return out;
    }
    else
    {
        context.parse_error("No input");
        return out;
    }
}

value parse(std::istream& input)
{
    // Copy the input into a buffer
    input.seekg(0, std::ios::end);
    std::size_t len = input.tellg();
    detail::shared_buffer buffer(len);
    input.seekg(0, std::ios::beg);
    std::copy(std::istreambuf_iterator<char>(input), std::istreambuf_iterator<char>(),
              buffer.get_mutable(0, len)
             );

    return parse(buffer.cbegin(), buffer.size());
}

value parse(const std::string& source)
{
    return parse(source.c_str(), source.size());
}

}
<|endoftext|>


<commit_before>/****************************************************************/
/* MOOSE - Multiphysics Object Oriented Simulation Environment */
/* */
/* All contents are licensed under LGPL V2.1 */
/* See LICENSE for full restrictions */
/****************************************************************/

//This kernel adds electromigration flux in the Cahn-Hilliard Equation
//Mathematically it is J_em=c*(D/kT)*z*eE
#include "SplitCHVoltage.h"
template<>
InputParameters validParams<SplitCHVoltage>()
{
InputParameters params = validParams<Kernel>();
params.addClassDescription("Add Electromigration Flux to Split formulation Cahn-Hilliard Kernel");
//params.addRequiredCoupledVar("T", "Temperature in Kelvin Scale");
params.addRequiredCoupledVar("volt", "Voltage");
params.addRequiredCoupledVar("c", "Concentration");
params.addRequiredParam<MaterialPropertyName>("diff_name", "The diffusivity used with the kernel");
params.addParam<MaterialPropertyName>("z_name", "zeff", "Effective charge number of the species");
//params.addParam<MaterialPropertyName>("e_name", "echarge", "Charge of the electron");
return params;
}
SplitCHVoltage::SplitCHVoltage(const std::string & name, InputParameters parameters) :
Kernel(name, parameters),
_T_var(coupled("T")),
_T(coupledValue("T")),
_grad_T(coupledGradient("T")),
_c_var(coupled("c")),
_c(coupledValue("c")),
_D(getMaterialProperty<Real>("diff_name")),
//_Q(getMaterialProperty<Real>("Q_name")),
_z(getMaterialProperty<Real>("z_name")),
_echarge(1.6e-19), // charge of an electron
_kb(8.617343e-5) // Boltzmann constant in eV/K

{
}
Real
SplitCHVoltage::computeQpResidual()
{
Real volt_term = _D[_qp] * _z[_qp] *_echarge * _c[_qp] / (_kb * _T[_qp] );
return volt_term * _grad_volt[_qp] * _grad_test[_i][_qp];
}
Real
SplitCHVoltage::computeQpOffDiagJacobian(unsigned int jvar)
{
if (_c_var == jvar)
return _D[_qp] * _z[_qp] *_echarge * _phi[_j][_qp] * _grad_volt[_qp] / (_kb *  _T[_qp]) * _grad_test[_i][_qp];
else if (_volt_var == jvar)
return _D[_qp] * _z[_qp] *_echarge * _c[_qp] * _grad_test[_i][_qp] *
(_grad_phi[_j][_qp]/(_kb * _T[_qp]) + _grad_volt[_qp] * _phi[_j][_qp] / (_kb * _T[_qp] * _T[_qp]));
return 0.0;
}
<commit_msg>Update SplitCHVoltage.C<commit_after>/****************************************************************/
/* MOOSE - Multiphysics Object Oriented Simulation Environment */
/* */
/* All contents are licensed under LGPL V2.1 */
/* See LICENSE for full restrictions */
/****************************************************************/

//This kernel adds electromigration flux in the Cahn-Hilliard Equation
//Mathematically it is J_em=c*(D/kT)*z*eE
#include "SplitCHVoltage.h"
template<>
InputParameters validParams<SplitCHVoltage>()
{
InputParameters params = validParams<Kernel>();
params.addClassDescription("Add Electromigration Flux to Split formulation Cahn-Hilliard Kernel");
//params.addRequiredCoupledVar("T", "Temperature in Kelvin Scale");
params.addRequiredCoupledVar("volt", "Voltage");
params.addRequiredCoupledVar("c", "Concentration");
params.addRequiredParam<MaterialPropertyName>("diff_name", "The diffusivity used with the kernel");
params.addRequiredParam<MaterialPropertyName>("T", "The temperature used with the kernel");
params.addParam<MaterialPropertyName>("z_name", "zeff", "Effective charge number of the species");
//params.addParam<MaterialPropertyName>("e_name", "eo", "Charge of the electron");
return params;
}
SplitCHVoltage::SplitCHVoltage(const std::string & name, InputParameters parameters) :
Kernel(name, parameters),
//_T_var(coupled("T")),
//_T(coupledValue("T")),
//_grad_T(coupledGradient("T")),
_volt_var(coupled("volt")),
_volt(coupledValue("volt")),
_grad_volt(coupledGradient("volt")),
_c_var(coupled("c")),
_c(coupledValue("c")),
_D(getMaterialProperty<Real>("diff_name")),
_T(getMaterialProperty<Real>("T")),
//_Q(getMaterialProperty<Real>("Q_name")),
_z(getMaterialProperty<Real>("z_name")),
//_T(getParam<Real>("T")),
_kb(8.617343e-5), // Boltzmann constant in eV/K
_eo(1.6e-19) // charge of an electron
{
}
Real
SplitCHVoltage::computeQpResidual()
{
Real volt_term = _D[_qp] * _z[_qp] *_eo * _c[_qp] / (_kb * _T[_qp] );
return volt_term * _grad_volt[_qp] * _grad_test[_i][_qp];
}

Real
SplitCHVoltage::computeQpJacobian()
{
  if (_c_var == _var.number()) //Requires c jacobian
    return computeQpCJacobian();

  return 0.0;
}

Real
SplitCHVoltage::computeQpOffDiagJacobian(unsigned int jvar)
{
if (_c_var == jvar)
return _D[_qp] * _z[_qp] * _eo * _phi[_j][_qp] * _grad_volt[_qp] / (_kb *  _T[_qp]) * _grad_test[_i][_qp];
else if (_volt_var == jvar)
return _D[_qp] * _z[_qp] * _eo * _c[_qp] * _grad_test[_i][_qp] *
(_grad_phi[_j][_qp]/(_kb * _T[_qp]) + _grad_volt[_qp] * _phi[_j][_qp] / (_kb * _T[_qp] * _T[_qp]));
return 0.0;
}

Real
SplitCHVoltage::computeQpCJacobian()
{
  //Calculate the Jacobian for the c variable
  return _D[_qp] * _z[_qp] * _phi[_j][_qp] * _grad_volt[_qp] / (_kb * _T[_qp]) * _grad_test[_i][_qp];
}
<|endoftext|>


<commit_before>/*
 * GeislerLayer.cpp
 *
 *  Created on: Apr 21, 2010
 *      Author: gkenyon
 */

#include "GeislerLayer.hpp"
#include <assert.h>
#include <float.h>

namespace PV {

GeislerLayer::GeislerLayer(const char* name, HyPerCol * hc)
  : V1(name, hc)
{
}

GeislerLayer::GeislerLayer(const char* name, HyPerCol * hc, PVLayerType type)
  : V1(name, hc)
{
}

int GeislerLayer::updateState(float time, float dt)
{
   PVParams * params = parent->parameters();

   pv_debug_info("[%d]: V1::updateState:", clayer->columnId);

   int spikingFlag = (int) params->value(name, "spikingFlag", 1);

   assert(spikingFlag == 0);

   const int nx = clayer->loc.nx;
   const int ny = clayer->loc.ny;
   const int nf = clayer->numFeatures;
   const int marginWidth = clayer->loc.nPad;

   pvdata_t * V = clayer->V;
   pvdata_t * phiExc   = clayer->phi[PHI_EXC];
   pvdata_t * phiInh  = clayer->phi[PHI_INH];
   pvdata_t * phiInhB   = clayer->phi[PHI_INHB];
   pvdata_t * activity = clayer->activity->data;

   // make sure activity in border is zero
   //
   for (int k = 0; k < clayer->numExtended; k++) {
      activity[k] = 0.0;
   }

   pvdata_t max_direct = -FLT_MAX;
   pvdata_t max_V = -FLT_MAX;

   // assume direct input to phiExc, lateral input to phiInh
   for (int k = 0; k < clayer->numNeurons; k++) {
      pvdata_t direct_input = phiExc[k];
      pvdata_t lateral_input = phiInh[k];
      V[k] = (direct_input > 0.0f) ? direct_input * lateral_input : direct_input;
      max_direct = ( max_direct > direct_input ) ? max_direct : direct_input;
      max_V = ( max_V > V[k] ) ? max_V : V[k];
      // reset accumulation buffers
      phiExc[k] = 0.0;
      phiInh[k] = 0.0;
      phiInhB[k] = 0.0;
   }

   pvdata_t scale_factor = fabs( max_direct ) / fabs( max_V + (max_V == 0.0f) );
   for (int k = 0; k < clayer->numNeurons; k++) {
      int kex = kIndexExtended(k, nx, ny, nf, marginWidth);
      V[k] *= scale_factor;
      activity[kex] = ( V[k] > 0.0f ) ? V[k] : 0.0f;
   }

   return 0;
}

}
<commit_msg>removed scaling<commit_after>/*
 * GeislerLayer.cpp
 *
 *  Created on: Apr 21, 2010
 *      Author: gkenyon
 */

#include "GeislerLayer.hpp"
#include <assert.h>
#include <float.h>

namespace PV {

GeislerLayer::GeislerLayer(const char* name, HyPerCol * hc)
  : V1(name, hc)
{
}

GeislerLayer::GeislerLayer(const char* name, HyPerCol * hc, PVLayerType type)
  : V1(name, hc)
{
}

int GeislerLayer::updateState(float time, float dt)
{
   PVParams * params = parent->parameters();

   pv_debug_info("[%d]: V1::updateState:", clayer->columnId);

   int spikingFlag = (int) params->value(name, "spikingFlag", 1);

   assert(spikingFlag == 0);

   const int nx = clayer->loc.nx;
   const int ny = clayer->loc.ny;
   const int nf = clayer->numFeatures;
   const int marginWidth = clayer->loc.nPad;

   pvdata_t * V = clayer->V;
   pvdata_t * phiExc   = clayer->phi[PHI_EXC];
   pvdata_t * phiInh  = clayer->phi[PHI_INH];
   pvdata_t * phiInhB   = clayer->phi[PHI_INHB];
   pvdata_t * activity = clayer->activity->data;

   // make sure activity in border is zero
   //
   for (int k = 0; k < clayer->numExtended; k++) {
      activity[k] = 0.0;
   }

   pvdata_t max_direct = -FLT_MAX;
   pvdata_t max_V = -FLT_MAX;

   // assume direct input to phiExc, lateral input to phiInh
   for (int k = 0; k < clayer->numNeurons; k++) {
      pvdata_t direct_input = phiExc[k];
      pvdata_t lateral_input = phiInh[k];
      V[k] = (direct_input > 0.0f) ? direct_input * lateral_input : direct_input;
      max_direct = ( max_direct > direct_input ) ? max_direct : direct_input;
      max_V = ( max_V > V[k] ) ? max_V : V[k];
      // reset accumulation buffers
      phiExc[k] = 0.0;
      phiInh[k] = 0.0;
      phiInhB[k] = 0.0;
   }

   pvdata_t scale_factor = fabs( max_direct ) / fabs( max_V + (max_V == 0.0f) );
   for (int k = 0; k < clayer->numNeurons; k++) {
      int kex = kIndexExtended(k, nx, ny, nf, marginWidth);
      //V[k] *= scale_factor;
      activity[kex] = ( V[k] > 0.0f ) ? V[k] : 0.0f;
   }

   return 0;
}

}
<|endoftext|>


<commit_before>/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
 * This file is part of the libetonyek project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include <libetonyek/libetonyek.h>

#include <boost/scoped_ptr.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/logic/tribool.hpp>

#include <libxml/xmlreader.h>

#include "libetonyek_utils.h"
#include "libetonyek_xml.h"
#include "IWORKPresentationRedirector.h"
#include "IWORKSpreadsheetRedirector.h"
#include "IWORKTextRedirector.h"
#include "IWORKTokenizer.h"
#include "IWORKZlibStream.h"
#include "KEY1Parser.h"
#include "KEY2Parser.h"
#include "KEY2Token.h"
#include "KEYCollector.h"
#include "KEYDictionary.h"
#include "NUMCollector.h"
#include "NUMDictionary.h"
#include "NUM1Parser.h"
#include "NUM1Token.h"
#include "PAGCollector.h"
#include "PAG1Parser.h"
#include "PAG1Token.h"
#include "PAGDictionary.h"

using boost::logic::indeterminate;
using boost::logic::tribool;
using boost::scoped_ptr;
using boost::shared_ptr;
using std::string;

using librevenge::RVNG_SEEK_SET;

namespace libetonyek
{

namespace
{

enum CheckType
{
  CHECK_TYPE_NONE = 0,
  CHECK_TYPE_KEYNOTE = 1 << 1,
  CHECK_TYPE_NUMBERS = 1 << 2,
  CHECK_TYPE_PAGES = 1 << 3,
  CHECK_TYPE_ANY = CHECK_TYPE_KEYNOTE | CHECK_TYPE_NUMBERS | CHECK_TYPE_PAGES
};

struct DetectionInfo
{
  DetectionInfo();

  RVNGInputStreamPtr_t input;
  RVNGInputStreamPtr_t package;
  EtonyekDocument::Confidence confidence;
  EtonyekDocument::Type type;
  unsigned version;
};

DetectionInfo::DetectionInfo()
  : input()
  , package()
  , confidence(EtonyekDocument::CONFIDENCE_NONE)
  , type(EtonyekDocument::TYPE_UNKNOWN)
  , version(0)
{
}

typedef bool (*ProbeXMLFun_t)(const RVNGInputStreamPtr_t &, unsigned &, xmlTextReaderPtr);

std::string queryAttribute(xmlTextReaderPtr reader, const int name, const int ns, const IWORKTokenizer &tokenizer)
{
  if (xmlTextReaderHasAttributes(reader))
  {
    int ret = xmlTextReaderMoveToFirstAttribute(reader);
    while (1 == ret)
    {
      const int id = tokenizer.getQualifiedId(char_cast(xmlTextReaderConstLocalName(reader)), char_cast(xmlTextReaderConstNamespaceUri(reader)));
      if ((ns | name) == id)
        return char_cast(xmlTextReaderConstValue(reader));

      ret = xmlTextReaderMoveToNextAttribute(reader);
    }
  }

  return "";
}

bool probeKeynote1XML(const RVNGInputStreamPtr_t &input, unsigned &version, xmlTextReaderPtr reader)
{
  // TODO: implement me
  (void) input;
  (void) version;
  (void) reader;
  return false;
}

bool probeKeynote2XML(const RVNGInputStreamPtr_t &input, unsigned &version, xmlTextReaderPtr reader)
{
  if (input->isEnd())
    return false;

  const IWORKTokenizer &tokenizer(KEY2Token::getTokenizer());
  assert(reader);

  const int id = tokenizer.getQualifiedId(char_cast(xmlTextReaderConstLocalName(reader)), char_cast(xmlTextReaderConstNamespaceUri(reader)));

  if ((KEY2Token::NS_URI_KEY | KEY2Token::presentation) == id)
  {
    const std::string v = queryAttribute(reader, KEY2Token::version, KEY2Token::NS_URI_KEY, tokenizer);

    switch (tokenizer.getId(v.c_str()))
    {
    case KEY2Token::VERSION_STR_2 :
      version = 2;
      return true;
    case KEY2Token::VERSION_STR_3 :
      version = 3;
      return true;
    case KEY2Token::VERSION_STR_4 :
      version = 4;
      return true;
    case KEY2Token::VERSION_STR_5 :
      version = 5;
      return true;
    }
  }

  return false;
}

bool probeKeynoteXML(const RVNGInputStreamPtr_t &input, unsigned &version, xmlTextReaderPtr reader)
{
  if (probeKeynote2XML(input, version, reader))
    return true;

  input->seek(0, RVNG_SEEK_SET);

  return probeKeynote1XML(input, version, reader);
}

bool probeNumbersXML(const RVNGInputStreamPtr_t &input, unsigned &version, xmlTextReaderPtr reader)
{
  if (input->isEnd())
    return false;

  const IWORKTokenizer &tokenizer(NUM1Token::getTokenizer());
  assert(reader);

  const int id = tokenizer.getQualifiedId(char_cast(xmlTextReaderConstLocalName(reader)), char_cast(xmlTextReaderConstNamespaceUri(reader)));

  if ((NUM1Token::NS_URI_LS | NUM1Token::document) == id)
  {
    const std::string v = queryAttribute(reader, NUM1Token::version, NUM1Token::NS_URI_LS, tokenizer);

    switch (tokenizer.getId(v.c_str()))
    {
    case NUM1Token::VERSION_STR_2 :
      version = 2;
      return true;
    }
  }

  return false;
}

bool probePagesXML(const RVNGInputStreamPtr_t &input, unsigned &version, xmlTextReaderPtr reader)
{
  if (input->isEnd())
    return false;

  const IWORKTokenizer &tokenizer(PAG1Token::getTokenizer());
  assert(reader);

  const int id = tokenizer.getQualifiedId(char_cast(xmlTextReaderConstLocalName(reader)), char_cast(xmlTextReaderConstNamespaceUri(reader)));

  if ((PAG1Token::NS_URI_SL | PAG1Token::document) == id)
  {
    const std::string v = queryAttribute(reader, PAG1Token::version, PAG1Token::NS_URI_SL, tokenizer);

    switch (tokenizer.getId(v.c_str()))
    {
    case PAG1Token::VERSION_STR_4 :
      version = 4;
      return true;
    }
  }

  return false;
}

bool probeXMLImpl(const RVNGInputStreamPtr_t &input, const ProbeXMLFun_t probe, const EtonyekDocument::Type type, DetectionInfo &info)
{
  const shared_ptr<xmlTextReader> reader(xmlReaderForIO(readFromStream, closeStream, input.get(), "", 0, 0), xmlFreeTextReader);
  if (!reader)
    return false;

  int ret = 0;
  do
  {
    ret = xmlTextReaderRead(reader.get());
  }
  while ((1 == ret) && (XML_READER_TYPE_ELEMENT != xmlTextReaderNodeType(reader.get())));

  if (1 != ret)
    return false;

  if (probe(input, info.version, reader.get()))
  {
    info.type = type;
    return true;
  }

  return false;
}

bool probeXML(const ProbeXMLFun_t probe, const EtonyekDocument::Type type, tribool &isGzipped, DetectionInfo &info)
{
  if (isGzipped || indeterminate(isGzipped))
  {
    try
    {
      const RVNGInputStreamPtr_t uncompressed(new IWORKZlibStream(info.input));
      isGzipped = true;

      if (probeXMLImpl(uncompressed, probe, type, info))
      {
        info.input = uncompressed;
        return true;
      }
      else
      {
        return false; // compressed, but invalid format
      }
    }
    catch (...)
    {
      if (isGzipped) // decompression failed? most probably a broken file...
        return false;

      isGzipped = false;
    }

    info.input->seek(0, RVNG_SEEK_SET);
  }

  assert(isGzipped == false);

  return probeXMLImpl(info.input, probe, type, info);
}

bool detect(const RVNGInputStreamPtr_t &input, unsigned checkTypes, DetectionInfo &info)
{
  info.confidence = EtonyekDocument::CONFIDENCE_SUPPORTED_PART;
  bool isXML = true;
  tribool isGzipped = indeterminate;
  tribool isKeynote1 = indeterminate;

  if (input->isStructured())
  {
    info.package = input;

    // check which format it might be
    if (CHECK_TYPE_KEYNOTE & checkTypes)
    {
      if (input->existsSubStream("index.apxl"))
      {
        checkTypes = CHECK_TYPE_KEYNOTE;
        isGzipped = false;
        isKeynote1 = false;
        info.input.reset(input->getSubStreamByName("index.apxl"));
      }
      else if (input->existsSubStream("index.apxl.gz"))
      {
        checkTypes = CHECK_TYPE_KEYNOTE;
        isGzipped = true;
        isKeynote1 = false;
        info.input.reset(input->getSubStreamByName("index.apxl.gz"));
      }
      else if (input->existsSubStream("presentation.apxl"))
      {
        checkTypes = CHECK_TYPE_KEYNOTE;
        isGzipped = false;
        isKeynote1 = true;
        info.input.reset(input->getSubStreamByName("presentation.apxl"));
      }
      else if (input->existsSubStream("presentation.apxl.gz"))
      {
        checkTypes = CHECK_TYPE_KEYNOTE;
        isGzipped = true;
        isKeynote1 = true;
        info.input.reset(input->getSubStreamByName("presentation.apxl.gz"));
      }
    }

    if ((CHECK_TYPE_NUMBERS | CHECK_TYPE_PAGES) & checkTypes)
    {
      if (input->existsSubStream("index.xml"))
      {
        checkTypes &= (CHECK_TYPE_NUMBERS | CHECK_TYPE_PAGES);
        isGzipped = false;
        info.input.reset(input->getSubStreamByName("index.xml"));
      }
      else if (input->existsSubStream("index.xml.gz"))
      {
        checkTypes &= (CHECK_TYPE_NUMBERS | CHECK_TYPE_PAGES);
        isGzipped = false;
        info.input.reset(input->getSubStreamByName("index.xml.gz"));
      }
    }

    if (!info.input && (CHECK_TYPE_ANY & checkTypes))
    {
      if (input->existsSubStream("Index.zip"))
      {
        isXML = false;
        info.input.reset(input->getSubStreamByName("Index.zip"));
      }
    }

    if (!info.input)
    {
      // nothing detected
      // TODO: this might also be Index.zip...
      return EtonyekDocument::CONFIDENCE_NONE;
    }

    info.confidence = EtonyekDocument::CONFIDENCE_EXCELLENT; // this is either a valid package of a false positive
  }
  else
  {
    info.input = input;
  }

  assert(bool(info.input));

  if (isXML)
  {
    assert(CHECK_TYPE_ANY & checkTypes);
    assert(!info.input->isStructured());

    info.input->seek(0, RVNG_SEEK_SET);

    if (CHECK_TYPE_KEYNOTE & checkTypes)
    {
      const ProbeXMLFun_t probe = (isKeynote1 ? probeKeynote1XML : ((!isKeynote1) ? probeKeynote2XML : probeKeynoteXML));
      if (probeXML(probe, EtonyekDocument::TYPE_KEYNOTE, isGzipped, info))
        return true;

      info.input->seek(0, RVNG_SEEK_SET);
    }

    if (CHECK_TYPE_NUMBERS & checkTypes)
    {
      if (probeXML(probeNumbersXML, EtonyekDocument::TYPE_NUMBERS, isGzipped, info))
        return true;

      info.input->seek(0, RVNG_SEEK_SET);
    }

    if (CHECK_TYPE_PAGES & checkTypes)
    {
      if (probeXML(probePagesXML, EtonyekDocument::TYPE_PAGES, isGzipped, info))
        return true;
    }
  }
  else
  {
    // TODO: detect type in binary format
  }

  return EtonyekDocument::CONFIDENCE_NONE;
}

}

namespace
{

shared_ptr<IWORKParser> makeKeynoteParser(const unsigned version, const RVNGInputStreamPtr_t &input, const RVNGInputStreamPtr_t &package, KEYCollector *const collector, KEYDictionary &dict)
{
  shared_ptr<IWORKParser> parser;

  if (1 == version)
    parser.reset(new KEY1Parser(input, package, collector));
  else if ((2 <= version) && (5 >= version))
    parser.reset(new KEY2Parser(input, package, collector, dict));
  else
    assert(0);

  return parser;
}

}

ETONYEKAPI EtonyekDocument::Confidence EtonyekDocument::isSupported(librevenge::RVNGInputStream *const input, EtonyekDocument::Type *type) try
{
  if (!input)
    return CONFIDENCE_NONE;

  if (type)
    *type = TYPE_UNKNOWN;

  DetectionInfo info;

  if (detect(RVNGInputStreamPtr_t(input, EtonyekDummyDeleter()), CHECK_TYPE_ANY, info))
  {
    assert(TYPE_UNKNOWN != info.type);
    assert(CONFIDENCE_NONE != info.confidence);
    assert(bool(info.input));

    if (type)
      *type = info.type;
    return info.confidence;
  }

  return CONFIDENCE_NONE;
}
catch (...)
{
  return CONFIDENCE_NONE;
}

ETONYEKAPI bool EtonyekDocument::parse(librevenge::RVNGInputStream *const input, librevenge::RVNGPresentationInterface *const generator) try
{
  if (!input || !generator)
    return false;

  DetectionInfo info;

  if (!detect(RVNGInputStreamPtr_t(input, EtonyekDummyDeleter()), CHECK_TYPE_KEYNOTE, info))
    return false;

  assert(TYPE_UNKNOWN != info.type);
  assert(CONFIDENCE_NONE != info.confidence);
  assert(bool(info.input));
  assert(0 != info.version);

  info.input->seek(0, librevenge::RVNG_SEEK_SET);

  KEYDictionary dict;
  IWORKPresentationRedirector redirector(generator);
  KEYCollector collector(&redirector);
  const shared_ptr<IWORKParser> parser = makeKeynoteParser(info.version, info.input, info.package, &collector, dict);
  return parser->parse();
}
catch (...)
{
  return false;
}

ETONYEKAPI bool EtonyekDocument::parse(librevenge::RVNGInputStream *const input, librevenge::RVNGSpreadsheetInterface *const document) try
{
  if (!input || !document)
    return false;

  DetectionInfo info;

  if (!detect(RVNGInputStreamPtr_t(input, EtonyekDummyDeleter()), CHECK_TYPE_NUMBERS, info))
    return false;

  assert(TYPE_UNKNOWN != info.type);
  assert(CONFIDENCE_NONE != info.confidence);
  assert(bool(info.input));
  assert(0 != info.version);

  info.input->seek(0, librevenge::RVNG_SEEK_SET);

  IWORKSpreadsheetRedirector redirector(document);
  NUMCollector collector(&redirector);
  NUMDictionary dict;
  NUM1Parser parser(info.input, info.package, &collector, &dict);
  return parser.parse();
}
catch (...)
{
  return false;
}

ETONYEKAPI bool EtonyekDocument::parse(librevenge::RVNGInputStream *const input, librevenge::RVNGTextInterface *const document) try
{
  if (!input || !document)
    return false;

  DetectionInfo info;

  if (!detect(RVNGInputStreamPtr_t(input, EtonyekDummyDeleter()), CHECK_TYPE_PAGES, info))
    return false;

  assert(TYPE_UNKNOWN != info.type);
  assert(CONFIDENCE_NONE != info.confidence);
  assert(bool(info.input));
  assert(0 != info.version);

  info.input->seek(0, librevenge::RVNG_SEEK_SET);

  IWORKTextRedirector redirector(document);
  PAGCollector collector(&redirector);
  PAGDictionary dict;
  PAG1Parser parser(info.input, info.package, &collector, &dict);
  return parser.parse();
}
catch (...)
{
  return false;
}

}

/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
<commit_msg>add include for assert()<commit_after>/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
 * This file is part of the libetonyek project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include <libetonyek/libetonyek.h>

#include <cassert>

#include <boost/scoped_ptr.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/logic/tribool.hpp>

#include <libxml/xmlreader.h>

#include "libetonyek_utils.h"
#include "libetonyek_xml.h"
#include "IWORKPresentationRedirector.h"
#include "IWORKSpreadsheetRedirector.h"
#include "IWORKTextRedirector.h"
#include "IWORKTokenizer.h"
#include "IWORKZlibStream.h"
#include "KEY1Parser.h"
#include "KEY2Parser.h"
#include "KEY2Token.h"
#include "KEYCollector.h"
#include "KEYDictionary.h"
#include "NUMCollector.h"
#include "NUMDictionary.h"
#include "NUM1Parser.h"
#include "NUM1Token.h"
#include "PAGCollector.h"
#include "PAG1Parser.h"
#include "PAG1Token.h"
#include "PAGDictionary.h"

using boost::logic::indeterminate;
using boost::logic::tribool;
using boost::scoped_ptr;
using boost::shared_ptr;
using std::string;

using librevenge::RVNG_SEEK_SET;

namespace libetonyek
{

namespace
{

enum CheckType
{
  CHECK_TYPE_NONE = 0,
  CHECK_TYPE_KEYNOTE = 1 << 1,
  CHECK_TYPE_NUMBERS = 1 << 2,
  CHECK_TYPE_PAGES = 1 << 3,
  CHECK_TYPE_ANY = CHECK_TYPE_KEYNOTE | CHECK_TYPE_NUMBERS | CHECK_TYPE_PAGES
};

struct DetectionInfo
{
  DetectionInfo();

  RVNGInputStreamPtr_t input;
  RVNGInputStreamPtr_t package;
  EtonyekDocument::Confidence confidence;
  EtonyekDocument::Type type;
  unsigned version;
};

DetectionInfo::DetectionInfo()
  : input()
  , package()
  , confidence(EtonyekDocument::CONFIDENCE_NONE)
  , type(EtonyekDocument::TYPE_UNKNOWN)
  , version(0)
{
}

typedef bool (*ProbeXMLFun_t)(const RVNGInputStreamPtr_t &, unsigned &, xmlTextReaderPtr);

std::string queryAttribute(xmlTextReaderPtr reader, const int name, const int ns, const IWORKTokenizer &tokenizer)
{
  if (xmlTextReaderHasAttributes(reader))
  {
    int ret = xmlTextReaderMoveToFirstAttribute(reader);
    while (1 == ret)
    {
      const int id = tokenizer.getQualifiedId(char_cast(xmlTextReaderConstLocalName(reader)), char_cast(xmlTextReaderConstNamespaceUri(reader)));
      if ((ns | name) == id)
        return char_cast(xmlTextReaderConstValue(reader));

      ret = xmlTextReaderMoveToNextAttribute(reader);
    }
  }

  return "";
}

bool probeKeynote1XML(const RVNGInputStreamPtr_t &input, unsigned &version, xmlTextReaderPtr reader)
{
  // TODO: implement me
  (void) input;
  (void) version;
  (void) reader;
  return false;
}

bool probeKeynote2XML(const RVNGInputStreamPtr_t &input, unsigned &version, xmlTextReaderPtr reader)
{
  if (input->isEnd())
    return false;

  const IWORKTokenizer &tokenizer(KEY2Token::getTokenizer());
  assert(reader);

  const int id = tokenizer.getQualifiedId(char_cast(xmlTextReaderConstLocalName(reader)), char_cast(xmlTextReaderConstNamespaceUri(reader)));

  if ((KEY2Token::NS_URI_KEY | KEY2Token::presentation) == id)
  {
    const std::string v = queryAttribute(reader, KEY2Token::version, KEY2Token::NS_URI_KEY, tokenizer);

    switch (tokenizer.getId(v.c_str()))
    {
    case KEY2Token::VERSION_STR_2 :
      version = 2;
      return true;
    case KEY2Token::VERSION_STR_3 :
      version = 3;
      return true;
    case KEY2Token::VERSION_STR_4 :
      version = 4;
      return true;
    case KEY2Token::VERSION_STR_5 :
      version = 5;
      return true;
    }
  }

  return false;
}

bool probeKeynoteXML(const RVNGInputStreamPtr_t &input, unsigned &version, xmlTextReaderPtr reader)
{
  if (probeKeynote2XML(input, version, reader))
    return true;

  input->seek(0, RVNG_SEEK_SET);

  return probeKeynote1XML(input, version, reader);
}

bool probeNumbersXML(const RVNGInputStreamPtr_t &input, unsigned &version, xmlTextReaderPtr reader)
{
  if (input->isEnd())
    return false;

  const IWORKTokenizer &tokenizer(NUM1Token::getTokenizer());
  assert(reader);

  const int id = tokenizer.getQualifiedId(char_cast(xmlTextReaderConstLocalName(reader)), char_cast(xmlTextReaderConstNamespaceUri(reader)));

  if ((NUM1Token::NS_URI_LS | NUM1Token::document) == id)
  {
    const std::string v = queryAttribute(reader, NUM1Token::version, NUM1Token::NS_URI_LS, tokenizer);

    switch (tokenizer.getId(v.c_str()))
    {
    case NUM1Token::VERSION_STR_2 :
      version = 2;
      return true;
    }
  }

  return false;
}

bool probePagesXML(const RVNGInputStreamPtr_t &input, unsigned &version, xmlTextReaderPtr reader)
{
  if (input->isEnd())
    return false;

  const IWORKTokenizer &tokenizer(PAG1Token::getTokenizer());
  assert(reader);

  const int id = tokenizer.getQualifiedId(char_cast(xmlTextReaderConstLocalName(reader)), char_cast(xmlTextReaderConstNamespaceUri(reader)));

  if ((PAG1Token::NS_URI_SL | PAG1Token::document) == id)
  {
    const std::string v = queryAttribute(reader, PAG1Token::version, PAG1Token::NS_URI_SL, tokenizer);

    switch (tokenizer.getId(v.c_str()))
    {
    case PAG1Token::VERSION_STR_4 :
      version = 4;
      return true;
    }
  }

  return false;
}

bool probeXMLImpl(const RVNGInputStreamPtr_t &input, const ProbeXMLFun_t probe, const EtonyekDocument::Type type, DetectionInfo &info)
{
  const shared_ptr<xmlTextReader> reader(xmlReaderForIO(readFromStream, closeStream, input.get(), "", 0, 0), xmlFreeTextReader);
  if (!reader)
    return false;

  int ret = 0;
  do
  {
    ret = xmlTextReaderRead(reader.get());
  }
  while ((1 == ret) && (XML_READER_TYPE_ELEMENT != xmlTextReaderNodeType(reader.get())));

  if (1 != ret)
    return false;

  if (probe(input, info.version, reader.get()))
  {
    info.type = type;
    return true;
  }

  return false;
}

bool probeXML(const ProbeXMLFun_t probe, const EtonyekDocument::Type type, tribool &isGzipped, DetectionInfo &info)
{
  if (isGzipped || indeterminate(isGzipped))
  {
    try
    {
      const RVNGInputStreamPtr_t uncompressed(new IWORKZlibStream(info.input));
      isGzipped = true;

      if (probeXMLImpl(uncompressed, probe, type, info))
      {
        info.input = uncompressed;
        return true;
      }
      else
      {
        return false; // compressed, but invalid format
      }
    }
    catch (...)
    {
      if (isGzipped) // decompression failed? most probably a broken file...
        return false;

      isGzipped = false;
    }

    info.input->seek(0, RVNG_SEEK_SET);
  }

  assert(isGzipped == false);

  return probeXMLImpl(info.input, probe, type, info);
}

bool detect(const RVNGInputStreamPtr_t &input, unsigned checkTypes, DetectionInfo &info)
{
  info.confidence = EtonyekDocument::CONFIDENCE_SUPPORTED_PART;
  bool isXML = true;
  tribool isGzipped = indeterminate;
  tribool isKeynote1 = indeterminate;

  if (input->isStructured())
  {
    info.package = input;

    // check which format it might be
    if (CHECK_TYPE_KEYNOTE & checkTypes)
    {
      if (input->existsSubStream("index.apxl"))
      {
        checkTypes = CHECK_TYPE_KEYNOTE;
        isGzipped = false;
        isKeynote1 = false;
        info.input.reset(input->getSubStreamByName("index.apxl"));
      }
      else if (input->existsSubStream("index.apxl.gz"))
      {
        checkTypes = CHECK_TYPE_KEYNOTE;
        isGzipped = true;
        isKeynote1 = false;
        info.input.reset(input->getSubStreamByName("index.apxl.gz"));
      }
      else if (input->existsSubStream("presentation.apxl"))
      {
        checkTypes = CHECK_TYPE_KEYNOTE;
        isGzipped = false;
        isKeynote1 = true;
        info.input.reset(input->getSubStreamByName("presentation.apxl"));
      }
      else if (input->existsSubStream("presentation.apxl.gz"))
      {
        checkTypes = CHECK_TYPE_KEYNOTE;
        isGzipped = true;
        isKeynote1 = true;
        info.input.reset(input->getSubStreamByName("presentation.apxl.gz"));
      }
    }

    if ((CHECK_TYPE_NUMBERS | CHECK_TYPE_PAGES) & checkTypes)
    {
      if (input->existsSubStream("index.xml"))
      {
        checkTypes &= (CHECK_TYPE_NUMBERS | CHECK_TYPE_PAGES);
        isGzipped = false;
        info.input.reset(input->getSubStreamByName("index.xml"));
      }
      else if (input->existsSubStream("index.xml.gz"))
      {
        checkTypes &= (CHECK_TYPE_NUMBERS | CHECK_TYPE_PAGES);
        isGzipped = false;
        info.input.reset(input->getSubStreamByName("index.xml.gz"));
      }
    }

    if (!info.input && (CHECK_TYPE_ANY & checkTypes))
    {
      if (input->existsSubStream("Index.zip"))
      {
        isXML = false;
        info.input.reset(input->getSubStreamByName("Index.zip"));
      }
    }

    if (!info.input)
    {
      // nothing detected
      // TODO: this might also be Index.zip...
      return EtonyekDocument::CONFIDENCE_NONE;
    }

    info.confidence = EtonyekDocument::CONFIDENCE_EXCELLENT; // this is either a valid package of a false positive
  }
  else
  {
    info.input = input;
  }

  assert(bool(info.input));

  if (isXML)
  {
    assert(CHECK_TYPE_ANY & checkTypes);
    assert(!info.input->isStructured());

    info.input->seek(0, RVNG_SEEK_SET);

    if (CHECK_TYPE_KEYNOTE & checkTypes)
    {
      const ProbeXMLFun_t probe = (isKeynote1 ? probeKeynote1XML : ((!isKeynote1) ? probeKeynote2XML : probeKeynoteXML));
      if (probeXML(probe, EtonyekDocument::TYPE_KEYNOTE, isGzipped, info))
        return true;

      info.input->seek(0, RVNG_SEEK_SET);
    }

    if (CHECK_TYPE_NUMBERS & checkTypes)
    {
      if (probeXML(probeNumbersXML, EtonyekDocument::TYPE_NUMBERS, isGzipped, info))
        return true;

      info.input->seek(0, RVNG_SEEK_SET);
    }

    if (CHECK_TYPE_PAGES & checkTypes)
    {
      if (probeXML(probePagesXML, EtonyekDocument::TYPE_PAGES, isGzipped, info))
        return true;
    }
  }
  else
  {
    // TODO: detect type in binary format
  }

  return EtonyekDocument::CONFIDENCE_NONE;
}

}

namespace
{

shared_ptr<IWORKParser> makeKeynoteParser(const unsigned version, const RVNGInputStreamPtr_t &input, const RVNGInputStreamPtr_t &package, KEYCollector *const collector, KEYDictionary &dict)
{
  shared_ptr<IWORKParser> parser;

  if (1 == version)
    parser.reset(new KEY1Parser(input, package, collector));
  else if ((2 <= version) && (5 >= version))
    parser.reset(new KEY2Parser(input, package, collector, dict));
  else
    assert(0);

  return parser;
}

}

ETONYEKAPI EtonyekDocument::Confidence EtonyekDocument::isSupported(librevenge::RVNGInputStream *const input, EtonyekDocument::Type *type) try
{
  if (!input)
    return CONFIDENCE_NONE;

  if (type)
    *type = TYPE_UNKNOWN;

  DetectionInfo info;

  if (detect(RVNGInputStreamPtr_t(input, EtonyekDummyDeleter()), CHECK_TYPE_ANY, info))
  {
    assert(TYPE_UNKNOWN != info.type);
    assert(CONFIDENCE_NONE != info.confidence);
    assert(bool(info.input));

    if (type)
      *type = info.type;
    return info.confidence;
  }

  return CONFIDENCE_NONE;
}
catch (...)
{
  return CONFIDENCE_NONE;
}

ETONYEKAPI bool EtonyekDocument::parse(librevenge::RVNGInputStream *const input, librevenge::RVNGPresentationInterface *const generator) try
{
  if (!input || !generator)
    return false;

  DetectionInfo info;

  if (!detect(RVNGInputStreamPtr_t(input, EtonyekDummyDeleter()), CHECK_TYPE_KEYNOTE, info))
    return false;

  assert(TYPE_UNKNOWN != info.type);
  assert(CONFIDENCE_NONE != info.confidence);
  assert(bool(info.input));
  assert(0 != info.version);

  info.input->seek(0, librevenge::RVNG_SEEK_SET);

  KEYDictionary dict;
  IWORKPresentationRedirector redirector(generator);
  KEYCollector collector(&redirector);
  const shared_ptr<IWORKParser> parser = makeKeynoteParser(info.version, info.input, info.package, &collector, dict);
  return parser->parse();
}
catch (...)
{
  return false;
}

ETONYEKAPI bool EtonyekDocument::parse(librevenge::RVNGInputStream *const input, librevenge::RVNGSpreadsheetInterface *const document) try
{
  if (!input || !document)
    return false;

  DetectionInfo info;

  if (!detect(RVNGInputStreamPtr_t(input, EtonyekDummyDeleter()), CHECK_TYPE_NUMBERS, info))
    return false;

  assert(TYPE_UNKNOWN != info.type);
  assert(CONFIDENCE_NONE != info.confidence);
  assert(bool(info.input));
  assert(0 != info.version);

  info.input->seek(0, librevenge::RVNG_SEEK_SET);

  IWORKSpreadsheetRedirector redirector(document);
  NUMCollector collector(&redirector);
  NUMDictionary dict;
  NUM1Parser parser(info.input, info.package, &collector, &dict);
  return parser.parse();
}
catch (...)
{
  return false;
}

ETONYEKAPI bool EtonyekDocument::parse(librevenge::RVNGInputStream *const input, librevenge::RVNGTextInterface *const document) try
{
  if (!input || !document)
    return false;

  DetectionInfo info;

  if (!detect(RVNGInputStreamPtr_t(input, EtonyekDummyDeleter()), CHECK_TYPE_PAGES, info))
    return false;

  assert(TYPE_UNKNOWN != info.type);
  assert(CONFIDENCE_NONE != info.confidence);
  assert(bool(info.input));
  assert(0 != info.version);

  info.input->seek(0, librevenge::RVNG_SEEK_SET);

  IWORKTextRedirector redirector(document);
  PAGCollector collector(&redirector);
  PAGDictionary dict;
  PAG1Parser parser(info.input, info.package, &collector, &dict);
  return parser.parse();
}
catch (...)
{
  return false;
}

}

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


<commit_before>#include "logger.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <time.h>

#ifndef LOG_DISABLED

#ifdef __unix__
#include <unistd.h>
#define MAX_PATH 255
#else
#include <windows.h>
#endif

static FILE *logFile = NULL;

static void timenow(char * buffer) {
	time_t rawtime;
	struct tm *timeinfo;

	time(&rawtime);
	timeinfo = localtime(&rawtime);

	strftime(buffer, 64, "%Y-%m-%d %H:%M:%S", timeinfo);
}

static void getLogFname(char* logpath) {
#ifdef __unix__
	const char *folder = getenv("TMPDIR");
	if (folder == nullptr) {
		folder = "/tmp";
	}
	strncpy(logpath, folder, MAX_PATH);
	strncat(logpath, "/open-license.log", MAX_PATH - strlen(logpath));
#else
	const int plen = GetTempPath(MAX_PATH, logpath);
	if(plen == 0) {
		fprintf(stderr, "Error getting temporary directory path");
	}
	strncat(logpath, "open-license.log", MAX_PATH - strlen(logpath));
#endif
}

void _log(const char* format, ...) {
	va_list args;
	char * buffer;
	if (logFile == NULL) {
		char logpath[MAX_PATH];
		getLogFname(logpath);
		logFile = fopen(logpath, "a");
		if (logFile == NULL) {
			return;
		}
	}
	buffer = (char *) malloc(sizeof(char) * strlen(format) + 64);
	timenow(buffer);
	sprintf(&buffer[strlen(buffer) - 1], "-[%d]-", getpid());
	strcat(buffer, format);
	va_start(args, format);
	vfprintf(logFile, buffer, args);
	va_end(args);
	free(buffer);
}

void _shutdown_log() {
	if (logFile != NULL) {
		fclose(logFile);
		logFile = NULL;
	}
}
#endif
<commit_msg>fix windows issue<commit_after>#include "logger.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <time.h>

#ifndef LOG_DISABLED

#ifdef __unix__
#include <unistd.h>
#define MAX_PATH 255
#else
#include <windows.h>
#endif

static FILE *logFile = NULL;

static void timenow(char * buffer) {
	time_t rawtime;
	struct tm *timeinfo;

	time(&rawtime);
	timeinfo = localtime(&rawtime);

	strftime(buffer, 64, "%Y-%m-%d %H:%M:%S", timeinfo);
}

static void getLogFname(char* logpath) {
#ifdef __unix__
	const char *folder = getenv("TMPDIR");
	if (folder == nullptr) {
		folder = "/tmp";
	}
	strncpy(logpath, folder, MAX_PATH);
	strncat(logpath, "/open-license.log", MAX_PATH - strlen(logpath));
#else
	const int plen = GetTempPath(MAX_PATH, logpath);
	if(plen == 0) {
		fprintf(stderr, "Error getting temporary directory path");
	}
	strncat(logpath, "open-license.log", MAX_PATH - strlen(logpath));
#endif
}

void _log(const char* format, ...) {
	va_list args;
	char * buffer;
	if (logFile == NULL) {
		char logpath[MAX_PATH];
		getLogFname(logpath);
		logFile = fopen(logpath, "a");
		if (logFile == NULL) {
			return;
		}
	}
	buffer = (char *) malloc(sizeof(char) * strlen(format) + 64);
	timenow(buffer);
	strcat(buffer, format);
	va_start(args, format);
	vfprintf(logFile, buffer, args);
	va_end(args);
	free(buffer);
}

void _shutdown_log() {
	if (logFile != NULL) {
		fclose(logFile);
		logFile = NULL;
	}
}
#endif
<|endoftext|>


<commit_before>#include <pb_view.h>
#include <pb_controller.h>
#include <poddlthread.h>
#include <download.h>
#include <config.h>
#include <logger.h>

#include <dllist.h>
#include <help.h>
#include <utils.h>

#include <sstream>
#include <iostream>
#include <cstring>

using namespace newsbeuter;

namespace podbeuter {

pb_view::pb_view(pb_controller * c) : ctrl(c), dllist_form(dllist_str), help_form(help_str), keys(0) {
}

pb_view::~pb_view() {
	stfl::reset();
}

void pb_view::run(bool auto_download) {
	bool quit = false;

	set_dllist_keymap_hint();

	do {

		if (ctrl->view_update_necessary()) {

			double total_kbps = ctrl->get_total_kbps();

			char parbuf[128] = "";
			if (ctrl->get_maxdownloads() > 1) {
				snprintf(parbuf, sizeof(parbuf), _(" - %u parallel downloads"), ctrl->get_maxdownloads());
			}

			char buf[1024];
			snprintf(buf, sizeof(buf), _("Queue (%u downloads in progress, %u total) - %.2f kb/s total%s"),
				static_cast<unsigned int>(ctrl->downloads_in_progress()),
				static_cast<unsigned int>(ctrl->downloads().size()), total_kbps, parbuf);

			dllist_form.set("head", buf);

			LOG(LOG_DEBUG, "pb_view::run: updating view... downloads().size() = %u", ctrl->downloads().size());

			if (ctrl->downloads().size() > 0) {

				std::string code = "{list";

				unsigned int i = 0;
				for (std::vector<download>::iterator it=ctrl->downloads().begin();it!=ctrl->downloads().end();++it,++i) {
					char lbuf[1024];
					snprintf(lbuf, sizeof(lbuf), " %4u [%6.1fMB/%6.1fMB] [%5.1f %%] [%7.2f kb/s] %-20s %s -> %s", i+1, it->current_size()/(1024*1024), it->total_size()/(1024*1024), it->percents_finished(), it->kbps(), it->status_text(), it->url(), it->filename());
					code.append(utils::strprintf("{listitem[%u] text:%s}", i, stfl::quote(lbuf).c_str()));
				}

				code.append("}");

				dllist_form.modify("dls", "replace_inner", code);
			}

			ctrl->set_view_update_necessary(false);
		}

		const char * event = dllist_form.run(500);

		if (auto_download) {
			if (ctrl->get_maxdownloads() > ctrl->downloads_in_progress()) {
				ctrl->start_downloads();
			}
		}

		if (!event || strcmp(event,"TIMEOUT")==0) continue;

		operation op = keys->get_operation(event, "podbeuter");

		if (dllist_form.get("msg").length() > 0) {
			dllist_form.set("msg", "");
			ctrl->set_view_update_necessary(true);
		}

		switch (op) {
			case OP_PB_TOGGLE_DLALL:
				auto_download = !auto_download;
				break;
			case OP_HARDQUIT:
			case OP_QUIT:
				if (ctrl->downloads_in_progress() > 0) {
					dllist_form.set("msg", _("Error: can't quit: download(s) in progress."));
					ctrl->set_view_update_necessary(true);
				} else {
					quit = true;
				}
				break;
			case OP_PB_MOREDL:
				ctrl->increase_parallel_downloads();
				break;
			case OP_PB_LESSDL:
				ctrl->decrease_parallel_downloads();
				break;
			case OP_PB_DOWNLOAD: {
					std::istringstream os(dllist_form.get("dlposname"));
					int idx = -1;
					os >> idx;
					if (idx != -1) {
						if (ctrl->downloads()[idx].status() != DL_DOWNLOADING) {
							poddlthread * thread = new poddlthread(&ctrl->downloads()[idx], ctrl->get_cfgcont());
							thread->start();
						}
					}
				}
				break;
			case OP_PB_PLAY: {
					std::istringstream os(dllist_form.get("dlposname"));
					int idx = -1;
					os >> idx;
					if (idx != -1) {
						dlstatus_t status = ctrl->downloads()[idx].status();
						if (status == DL_FINISHED || status == DL_PLAYED) {
							ctrl->play_file(ctrl->downloads()[idx].filename());
							ctrl->downloads()[idx].set_status(DL_PLAYED);
						} else {
							dllist_form.set("msg", _("Error: download needs to be finished before the file can be played."));
						}
					}
				}
				break;
			case OP_PB_CANCEL: {
					std::istringstream os(dllist_form.get("dlposname"));
					int idx = -1;
					os >> idx;
					if (idx != -1) {
						if (ctrl->downloads()[idx].status() == DL_DOWNLOADING) {
							ctrl->downloads()[idx].set_status(DL_CANCELLED);
						}
					}
				}
				break;
			case OP_PB_DELETE: {
					std::istringstream os(dllist_form.get("dlposname"));
					int idx = -1;
					os >> idx;
					if (idx != -1) {
						if (ctrl->downloads()[idx].status() != DL_DOWNLOADING) {
							ctrl->downloads()[idx].set_status(DL_DELETED);
						}
					}
				}
				break;
			case OP_PB_PURGE:
				if (ctrl->downloads_in_progress() > 0) {
					dllist_form.set("msg", _("Error: unable to perform operation: download(s) in progress."));
				} else {
					ctrl->reload_queue(true);
				}
				ctrl->set_view_update_necessary(true);
				break;
			case OP_HELP:
				run_help();
				break;
			default:
				break;
		}

	} while (!quit);
}

void pb_view::set_bindings() {
	if (keys) {
		std::string upkey("** "); upkey.append(keys->getkey(OP_SK_UP, "podbeuter"));
		std::string downkey("** "); downkey.append(keys->getkey(OP_SK_DOWN, "podbeuter"));
		std::string pgupkey("** "); pgupkey.append(keys->getkey(OP_SK_PGUP, "podbeuter"));
		std::string pgdownkey("** "); pgdownkey.append(keys->getkey(OP_SK_PGDOWN, "podbeuter"));

		dllist_form.set("bind_up", upkey);
		dllist_form.set("bind_down", downkey);
		dllist_form.set("bind_page_up", pgupkey);
		dllist_form.set("bind_page_down", pgdownkey);

		help_form.set("bind_up", upkey);
		help_form.set("bind_down", downkey);
		help_form.set("bind_page_up", pgupkey);
		help_form.set("bind_page_down", pgdownkey);

	}
}

void pb_view::run_help() {
	set_help_keymap_hint();

	help_form.set("head",_("Help"));

	std::vector<keymap_desc> descs;
	keys->get_keymap_descriptions(descs, KM_PODBEUTER);

	std::string code = "{list";

	for (std::vector<keymap_desc>::iterator it=descs.begin();it!=descs.end();++it) {
		std::string line = "{listitem text:";

		std::string descline;
		descline.append(it->key);
		descline.append(8-it->key.length(),' ');
		descline.append(it->cmd);
		descline.append(24-it->cmd.length(),' ');
		descline.append(it->desc);
		line.append(stfl::quote(descline));
		line.append("}");

		code.append(line);
	}

	code.append("}");

	help_form.modify("helptext","replace_inner",code);

	bool quit = false;

	do {
		const char * event = help_form.run(0);
		if (!event) continue;

		operation op = keys->get_operation(event, "help");

		switch (op) {
			case OP_HARDQUIT:
			case OP_QUIT:
				quit = true;
				break;
			default:
				break;
		}
	} while (!quit);
}

std::string pb_view::prepare_keymaphint(keymap_hint_entry * hints) {
	std::string keymap_hint;
	for (int i=0;hints[i].op != OP_NIL; ++i) {
		keymap_hint.append(keys->getkey(hints[i].op, "podbeuter"));
		keymap_hint.append(":");
		keymap_hint.append(hints[i].text);
		keymap_hint.append(" ");
	}
	return keymap_hint;
}

void pb_view::set_help_keymap_hint() {
	keymap_hint_entry hints[] = {
		{ OP_QUIT, _("Quit") },
		{ OP_NIL, NULL }
	};
	std::string keymap_hint = prepare_keymaphint(hints);
	help_form.set("help", keymap_hint);
}

void pb_view::set_dllist_keymap_hint() {
	keymap_hint_entry hints[] = {
		{ OP_QUIT, _("Quit") },
		{ OP_PB_DOWNLOAD, _("Download") },
		{ OP_PB_CANCEL, _("Cancel") },
		{ OP_PB_DELETE, _("Delete") },
		{ OP_PB_PURGE, _("Purge Finished") },
		{ OP_PB_TOGGLE_DLALL, _("Toggle Automatic Download") },
		{ OP_PB_PLAY, _("Play") },
		{ OP_HELP, _("Help") },
		{ OP_NIL, NULL }
	};

	std::string keymap_hint = prepare_keymaphint(hints);
	dllist_form.set("help", keymap_hint);
}

}
<commit_msg>bind home and end keys to built-in stfl actions<commit_after>#include <pb_view.h>
#include <pb_controller.h>
#include <poddlthread.h>
#include <download.h>
#include <config.h>
#include <logger.h>

#include <dllist.h>
#include <help.h>
#include <utils.h>

#include <sstream>
#include <iostream>
#include <cstring>

using namespace newsbeuter;

namespace podbeuter {

pb_view::pb_view(pb_controller * c) : ctrl(c), dllist_form(dllist_str), help_form(help_str), keys(0) {
}

pb_view::~pb_view() {
	stfl::reset();
}

void pb_view::run(bool auto_download) {
	bool quit = false;

	set_dllist_keymap_hint();

	do {

		if (ctrl->view_update_necessary()) {

			double total_kbps = ctrl->get_total_kbps();

			char parbuf[128] = "";
			if (ctrl->get_maxdownloads() > 1) {
				snprintf(parbuf, sizeof(parbuf), _(" - %u parallel downloads"), ctrl->get_maxdownloads());
			}

			char buf[1024];
			snprintf(buf, sizeof(buf), _("Queue (%u downloads in progress, %u total) - %.2f kb/s total%s"),
				static_cast<unsigned int>(ctrl->downloads_in_progress()),
				static_cast<unsigned int>(ctrl->downloads().size()), total_kbps, parbuf);

			dllist_form.set("head", buf);

			LOG(LOG_DEBUG, "pb_view::run: updating view... downloads().size() = %u", ctrl->downloads().size());

			if (ctrl->downloads().size() > 0) {

				std::string code = "{list";

				unsigned int i = 0;
				for (std::vector<download>::iterator it=ctrl->downloads().begin();it!=ctrl->downloads().end();++it,++i) {
					char lbuf[1024];
					snprintf(lbuf, sizeof(lbuf), " %4u [%6.1fMB/%6.1fMB] [%5.1f %%] [%7.2f kb/s] %-20s %s -> %s", i+1, it->current_size()/(1024*1024), it->total_size()/(1024*1024), it->percents_finished(), it->kbps(), it->status_text(), it->url(), it->filename());
					code.append(utils::strprintf("{listitem[%u] text:%s}", i, stfl::quote(lbuf).c_str()));
				}

				code.append("}");

				dllist_form.modify("dls", "replace_inner", code);
			}

			ctrl->set_view_update_necessary(false);
		}

		const char * event = dllist_form.run(500);

		if (auto_download) {
			if (ctrl->get_maxdownloads() > ctrl->downloads_in_progress()) {
				ctrl->start_downloads();
			}
		}

		if (!event || strcmp(event,"TIMEOUT")==0) continue;

		operation op = keys->get_operation(event, "podbeuter");

		if (dllist_form.get("msg").length() > 0) {
			dllist_form.set("msg", "");
			ctrl->set_view_update_necessary(true);
		}

		switch (op) {
			case OP_PB_TOGGLE_DLALL:
				auto_download = !auto_download;
				break;
			case OP_HARDQUIT:
			case OP_QUIT:
				if (ctrl->downloads_in_progress() > 0) {
					dllist_form.set("msg", _("Error: can't quit: download(s) in progress."));
					ctrl->set_view_update_necessary(true);
				} else {
					quit = true;
				}
				break;
			case OP_PB_MOREDL:
				ctrl->increase_parallel_downloads();
				break;
			case OP_PB_LESSDL:
				ctrl->decrease_parallel_downloads();
				break;
			case OP_PB_DOWNLOAD: {
					std::istringstream os(dllist_form.get("dlposname"));
					int idx = -1;
					os >> idx;
					if (idx != -1) {
						if (ctrl->downloads()[idx].status() != DL_DOWNLOADING) {
							poddlthread * thread = new poddlthread(&ctrl->downloads()[idx], ctrl->get_cfgcont());
							thread->start();
						}
					}
				}
				break;
			case OP_PB_PLAY: {
					std::istringstream os(dllist_form.get("dlposname"));
					int idx = -1;
					os >> idx;
					if (idx != -1) {
						dlstatus_t status = ctrl->downloads()[idx].status();
						if (status == DL_FINISHED || status == DL_PLAYED) {
							ctrl->play_file(ctrl->downloads()[idx].filename());
							ctrl->downloads()[idx].set_status(DL_PLAYED);
						} else {
							dllist_form.set("msg", _("Error: download needs to be finished before the file can be played."));
						}
					}
				}
				break;
			case OP_PB_CANCEL: {
					std::istringstream os(dllist_form.get("dlposname"));
					int idx = -1;
					os >> idx;
					if (idx != -1) {
						if (ctrl->downloads()[idx].status() == DL_DOWNLOADING) {
							ctrl->downloads()[idx].set_status(DL_CANCELLED);
						}
					}
				}
				break;
			case OP_PB_DELETE: {
					std::istringstream os(dllist_form.get("dlposname"));
					int idx = -1;
					os >> idx;
					if (idx != -1) {
						if (ctrl->downloads()[idx].status() != DL_DOWNLOADING) {
							ctrl->downloads()[idx].set_status(DL_DELETED);
						}
					}
				}
				break;
			case OP_PB_PURGE:
				if (ctrl->downloads_in_progress() > 0) {
					dllist_form.set("msg", _("Error: unable to perform operation: download(s) in progress."));
				} else {
					ctrl->reload_queue(true);
				}
				ctrl->set_view_update_necessary(true);
				break;
			case OP_HELP:
				run_help();
				break;
			default:
				break;
		}

	} while (!quit);
}

void pb_view::set_bindings() {
	if (keys) {
		std::string upkey("** "); upkey.append(keys->getkey(OP_SK_UP, "podbeuter"));
		std::string downkey("** "); downkey.append(keys->getkey(OP_SK_DOWN, "podbeuter"));
		std::string pgupkey("** "); pgupkey.append(keys->getkey(OP_SK_PGUP, "podbeuter"));
		std::string pgdownkey("** "); pgdownkey.append(keys->getkey(OP_SK_PGDOWN, "podbeuter"));
		std::string homekey("** "); homekey.append(keys->getkey(OP_SK_HOME, "podbeuter"));
		std::string endkey("** "); endkey.append(keys->getkey(OP_SK_END, "podbeuter"));

		dllist_form.set("bind_up", upkey);
		dllist_form.set("bind_down", downkey);
		dllist_form.set("bind_page_up", pgupkey);
		dllist_form.set("bind_page_down", pgdownkey);
		dllist_form.set("bind_home", homekey);
		dllist_form.set("bind_end", endkey);

		help_form.set("bind_up", upkey);
		help_form.set("bind_down", downkey);
		help_form.set("bind_page_up", pgupkey);
		help_form.set("bind_page_down", pgdownkey);
		help_form.set("bind_home", homekey);
		help_form.set("bind_end", endkey);

	}
}

void pb_view::run_help() {
	set_help_keymap_hint();

	help_form.set("head",_("Help"));

	std::vector<keymap_desc> descs;
	keys->get_keymap_descriptions(descs, KM_PODBEUTER);

	std::string code = "{list";

	for (std::vector<keymap_desc>::iterator it=descs.begin();it!=descs.end();++it) {
		std::string line = "{listitem text:";

		std::string descline;
		descline.append(it->key);
		descline.append(8-it->key.length(),' ');
		descline.append(it->cmd);
		descline.append(24-it->cmd.length(),' ');
		descline.append(it->desc);
		line.append(stfl::quote(descline));
		line.append("}");

		code.append(line);
	}

	code.append("}");

	help_form.modify("helptext","replace_inner",code);

	bool quit = false;

	do {
		const char * event = help_form.run(0);
		if (!event) continue;

		operation op = keys->get_operation(event, "help");

		switch (op) {
			case OP_HARDQUIT:
			case OP_QUIT:
				quit = true;
				break;
			default:
				break;
		}
	} while (!quit);
}

std::string pb_view::prepare_keymaphint(keymap_hint_entry * hints) {
	std::string keymap_hint;
	for (int i=0;hints[i].op != OP_NIL; ++i) {
		keymap_hint.append(keys->getkey(hints[i].op, "podbeuter"));
		keymap_hint.append(":");
		keymap_hint.append(hints[i].text);
		keymap_hint.append(" ");
	}
	return keymap_hint;
}

void pb_view::set_help_keymap_hint() {
	keymap_hint_entry hints[] = {
		{ OP_QUIT, _("Quit") },
		{ OP_NIL, NULL }
	};
	std::string keymap_hint = prepare_keymaphint(hints);
	help_form.set("help", keymap_hint);
}

void pb_view::set_dllist_keymap_hint() {
	keymap_hint_entry hints[] = {
		{ OP_QUIT, _("Quit") },
		{ OP_PB_DOWNLOAD, _("Download") },
		{ OP_PB_CANCEL, _("Cancel") },
		{ OP_PB_DELETE, _("Delete") },
		{ OP_PB_PURGE, _("Purge Finished") },
		{ OP_PB_TOGGLE_DLALL, _("Toggle Automatic Download") },
		{ OP_PB_PLAY, _("Play") },
		{ OP_HELP, _("Help") },
		{ OP_NIL, NULL }
	};

	std::string keymap_hint = prepare_keymaphint(hints);
	dllist_form.set("help", keymap_hint);
}

}
<|endoftext|>


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

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

#include <vector>
#include <iostream>
#include <iomanip>

#include "pefile.h"
#include "pemap.h"
#include "pepatch.h"

namespace {

/**
 * A range of memory to patch. This is used to keep track of what needs to be
 * patched in the PE file.
 *
 * All the patch locations need to be found before finishing parsing. If we
 * patched while parsing, then parsing could fail and we could be left with an
 * incomplete patch. Thus, we keep a list of patches and patch everything all at
 * once to mitigate failure cases.
 */
struct Patch
{
    // Location to patch.
    size_t offset;

    // Length of the data.
    size_t length;

    // Data overwrite the given location with.
    const uint8_t* data;

    // Name of the patch. Useful to see what's going on.
    const char* name;

    Patch(size_t offset, size_t length, const uint8_t* data, const char* name = NULL)
        : offset(offset),
          length(length),
          data(data),
          name(name)
    {}

    template<typename T>
    Patch(size_t offset, const T* data, const char* name = NULL)
        : offset(offset),
          length(sizeof(T)),
          data((const uint8_t*)data),
          name(name)
    {
    }

    friend std::ostream& operator<<(std::ostream& os, const Patch& patch);

    /**
     * Applies the patch. Note that no bounds checking is done. It is assumed
     * that it has already been done.
     */
    void apply(uint8_t* buf, bool dryRun) {
        std::cout << *this << std::endl;

        if (!dryRun) {
            for (size_t i = 0; i < length; ++i)
                buf[offset+i] = data[i];
        }
    }
};

std::ostream& operator<<(std::ostream& os, const Patch& patch) {
    os << "Patching '" << patch.name
       << "' at offset 0x" << std::hex << patch.offset << std::dec
       << " (" << patch.length << " bytes)";
    return os;
}

class Patches
{
private:
    // List of patches
    std::vector<Patch> _patches;

    uint8_t* _buf;

public:

    Patches(uint8_t* buf) : _buf(buf) {}

    void add(Patch patch) {
        _patches.push_back(patch);
    }

    template<typename T>
    void add(const T* addr, const T* data, const char* name = NULL)
    {
        add(Patch((const uint8_t*)addr - _buf, data, name));
    }

    void apply(bool dryRun = false) {
        for (auto&& patch: _patches)
            patch.apply(_buf, dryRun);
    }
};

/**
 * Helper class to parse image headers.
 */
class ImageHeaders
{
private:

    const uint8_t* _buf;
    const size_t _length;

    // Pointer to the optional header.
    const uint8_t* _optional;

    void _init();

public:

    const IMAGE_DOS_HEADER* dos;
    const IMAGE_FILE_HEADER* file;
    const IMAGE_SECTION_HEADER* sections;

    ImageHeaders(const uint8_t* buf, size_t length);

    /**
     * The Magic field of the optional header. This is used to determine if the
     * optional header is 32- or 64-bit.
     */
    uint16_t magic() const {
        return *(uint16_t*)_optional;
    }

    /**
     * Returns the optional header of the given type.
     */
    template<typename T>
    const T* optional() const {
        // Bounds check
        if (_optional + sizeof(T) >= _buf + _length)
            throw InvalidImage("missing IMAGE_OPTIONAL_HEADER");

        return (T*)_optional;
    }

    /**
     * Translates a relative virtual address (RVA) to a physical address within
     * the mapped file. Note that this does not do any bounds checking. You must
     * check the bounds before dereferencing the returned pointer.
     */
    const uint8_t* translate(size_t rva) const {

        const IMAGE_SECTION_HEADER* s = sections;

        for (size_t i = 0; i < file->NumberOfSections; ++i) {
            if (rva >= s->VirtualAddress &&
                rva < s->VirtualAddress + s->Misc.VirtualSize)
                break;

            ++s;
        }

        return _buf + rva - s->VirtualAddress + s->PointerToRawData;
    }

    /**
     * Checks if the given image address is valid.
     */
    bool isValidAddress(const uint8_t* p) const {
        return p >= _buf && p < _buf + _length;
    }
};

ImageHeaders::ImageHeaders(const uint8_t* buf, size_t length)
    : _buf(buf), _length(length)
{
    _init();
}

void ImageHeaders::_init() {

    const uint8_t* p = _buf;
    const uint8_t* end = _buf + _length;

    if (p + sizeof(IMAGE_DOS_HEADER) >= end)
        throw InvalidImage("missing DOS header");

    dos = (IMAGE_DOS_HEADER*)p;
    if (dos->e_magic != IMAGE_DOS_SIGNATURE)
        throw InvalidImage("invalid DOS signature");

    // Skip to the NT headers. Note that we don't parse this section as
    // IMAGE_NT_HEADERS32/IMAGE_NT_HEADERS64 because we don't yet know if this
    // image is 32- or 64-bit. That information is in the first field of the
    // optional header.
    p += dos->e_lfanew;

    //
    // Check the signature
    //
    if (p + sizeof(uint32_t) >= end)
        throw InvalidImage("missing PE signature");

    const uint32_t signature = *(uint32_t*)p;
    if (signature != *(const uint32_t*)"PE\0\0")
        throw InvalidImage("invalid PE signature");

    p += sizeof(signature);

    //
    // Parse the image file header
    //
    if (p + sizeof(IMAGE_FILE_HEADER) >= end)
        throw InvalidImage("missing IMAGE_FILE_HEADER");

    file = (IMAGE_FILE_HEADER*)p;

    p += sizeof(IMAGE_FILE_HEADER);

    //
    // The optional header is here. Parsing of this is delayed because it can
    // either be a 32- or 64-bit structure.
    //
    _optional = p;

    p += file->SizeOfOptionalHeader;

    //
    // Section headers. There are IMAGE_FILE_HEADER.NumberOfSections of these.
    //
    sections = (IMAGE_SECTION_HEADER*)p;
}

/**
 * Patches the timestamp associated with a data directory.
 */
template<typename T>
void patchDataDirectory(const ImageHeaders& headers, Patches& patches,
        const IMAGE_DATA_DIRECTORY* imageDataDirs,
        uint32_t entry, const char* name, uint32_t timestamp) {

    const IMAGE_DATA_DIRECTORY& imageDataDir = imageDataDirs[entry];

    // Doesn't exist? Nothing to patch.
    if (imageDataDir.VirtualAddress == 0)
        return;

    if (imageDataDir.Size < sizeof(T)) {
        // Note that we check if the size is less than our defined struct.
        // Microsoft is free to add elements to the end of the struct in future
        // versions as that still maintains ABI compatibility.
        throw InvalidImage("invalid IMAGE_DATA_DIRECTORY size");
    }

    const uint8_t* p = headers.translate(imageDataDir.VirtualAddress);
    if (!headers.isValidAddress(p))
        throw InvalidImage("invalid IMAGE_DATA_DIRECTORY offset");

    const T* dir = (const T*)p;

    if (dir->TimeDateStamp != 0)
        patches.add(&dir->TimeDateStamp, &timestamp, name);
}

/**
 * Patches the image based on the optional header type. The optional header can
 * be either 32- or 64-bit.
 */
template<typename T>
void patchOptionalHeader(const ImageHeaders& headers,
        Patches& patches, uint32_t timestamp) {

    const T* optional = headers.optional<T>();

    patches.add(&optional->CheckSum, &timestamp,
            "OptionalHeader.CheckSum");

    const IMAGE_DATA_DIRECTORY* dataDirs = optional->DataDirectory;

    // Patch exports directory timestamp
    patchDataDirectory<IMAGE_EXPORT_DIRECTORY>(headers, patches,
            dataDirs, IMAGE_DIRECTORY_ENTRY_EXPORT,
            "IMAGE_EXPORT_DIRECTORY.TimeDateStamp", timestamp);

    // Patch resource directory timestamp
    patchDataDirectory<IMAGE_RESOURCE_DIRECTORY>(headers, patches,
            dataDirs, IMAGE_DIRECTORY_ENTRY_RESOURCE,
            "IMAGE_RESOURCE_DIRECTORY.TimeDateStamp", timestamp);
}

}

void patchImage(const char* imagePath, const char* pdbPath, bool dryRun) {
    MemMap image(imagePath);
    MemMap pdb(pdbPath);

    // Replacement for timestamps
    const uint32_t timestamp = 0;

    uint8_t* buf = (uint8_t*)image.buf();
    const size_t length = image.length();

    ImageHeaders headers = ImageHeaders(buf, length);

    Patches patches(buf);

    if (length < sizeof(IMAGE_DOS_HEADER))
        throw InvalidImage("missing DOS header");

    patches.add(&headers.file->TimeDateStamp, &timestamp,
            "IMAGE_FILE_HEADER.TimeDateStamp");

    switch (headers.magic()) {
        case IMAGE_NT_OPTIONAL_HDR32_MAGIC:
            // Patch as a PE32 file
            patchOptionalHeader<IMAGE_OPTIONAL_HEADER32>(headers,
                    patches, timestamp);
            break;

        case IMAGE_NT_OPTIONAL_HDR64_MAGIC:
            // Patch as a PE32+ file
            patchOptionalHeader<IMAGE_OPTIONAL_HEADER64>(headers,
                    patches, timestamp);
            break;

        default:
            throw InvalidImage("unsupported IMAGE_NT_HEADERS.OptionalHeader");
    }

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

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

#include <vector>
#include <iostream>
#include <iomanip>

#include "pefile.h"
#include "pemap.h"
#include "pepatch.h"

namespace {

/**
 * A range of memory to patch. This is used to keep track of what needs to be
 * patched in the PE file.
 *
 * All the patch locations need to be found before finishing parsing. If we
 * patched while parsing, then parsing could fail and we could be left with an
 * incomplete patch. Thus, we keep a list of patches and patch everything all at
 * once to mitigate failure cases.
 */
struct Patch
{
    // Location to patch.
    size_t offset;

    // Length of the data.
    size_t length;

    // Data overwrite the given location with.
    const uint8_t* data;

    // Name of the patch. Useful to see what's going on.
    const char* name;

    Patch(size_t offset, size_t length, const uint8_t* data, const char* name = NULL)
        : offset(offset),
          length(length),
          data(data),
          name(name)
    {}

    template<typename T>
    Patch(size_t offset, const T* data, const char* name = NULL)
        : offset(offset),
          length(sizeof(T)),
          data((const uint8_t*)data),
          name(name)
    {
    }

    friend std::ostream& operator<<(std::ostream& os, const Patch& patch);

    /**
     * Applies the patch. Note that no bounds checking is done. It is assumed
     * that it has already been done.
     */
    void apply(uint8_t* buf, bool dryRun) {
        std::cout << *this << std::endl;

        if (!dryRun) {
            for (size_t i = 0; i < length; ++i)
                buf[offset+i] = data[i];
        }
    }
};

std::ostream& operator<<(std::ostream& os, const Patch& patch) {
    os << "Patching '" << patch.name
       << "' at offset 0x" << std::hex << patch.offset << std::dec
       << " (" << patch.length << " bytes)";
    return os;
}

class Patches
{
private:
    // List of patches
    std::vector<Patch> _patches;

    uint8_t* _buf;

public:

    Patches(uint8_t* buf) : _buf(buf) {}

    void add(Patch patch) {
        _patches.push_back(patch);
    }

    template<typename T>
    void add(const T* addr, const T* data, const char* name = NULL)
    {
        add(Patch((const uint8_t*)addr - _buf, data, name));
    }

    void apply(bool dryRun = false) {
        for (auto&& patch: _patches)
            patch.apply(_buf, dryRun);
    }
};

/**
 * Helper class to parse image headers.
 */
class PEFile
{
private:

    const uint8_t* _buf;
    const size_t _length;

    // Pointer to the optional header.
    const uint8_t* _optional;

    void _init();

public:

    const IMAGE_DOS_HEADER* dosHeader;
    const IMAGE_FILE_HEADER* fileHeader;
    const IMAGE_SECTION_HEADER* sectionHeaders;

    PEFile(const uint8_t* buf, size_t length);

    /**
     * The Magic field of the optional header. This is used to determine if the
     * optional header is 32- or 64-bit.
     */
    uint16_t magic() const {
        return *(uint16_t*)_optional;
    }

    /**
     * Returns the optional header of the given type.
     */
    template<typename T>
    const T* optional() const {
        // Bounds check
        if (_optional + sizeof(T) >= _buf + _length)
            throw InvalidImage("missing IMAGE_OPTIONAL_HEADER");

        return (T*)_optional;
    }

    /**
     * Translates a relative virtual address (RVA) to a physical address within
     * the mapped file. Note that this does not do any bounds checking. You must
     * check the bounds before dereferencing the returned pointer.
     */
    const uint8_t* translate(size_t rva) const {

        const IMAGE_SECTION_HEADER* s = sectionHeaders;

        for (size_t i = 0; i < fileHeader->NumberOfSections; ++i) {
            if (rva >= s->VirtualAddress &&
                rva < s->VirtualAddress + s->Misc.VirtualSize)
                break;

            ++s;
        }

        return _buf + rva - s->VirtualAddress + s->PointerToRawData;
    }

    /**
     * Checks if the given image address is valid.
     */
    bool isValidAddress(const uint8_t* p) const {
        return p >= _buf && p < _buf + _length;
    }
};

PEFile::PEFile(const uint8_t* buf, size_t length)
    : _buf(buf), _length(length)
{
    _init();
}

void PEFile::_init() {

    const uint8_t* p = _buf;
    const uint8_t* end = _buf + _length;

    if (p + sizeof(IMAGE_DOS_HEADER) >= end)
        throw InvalidImage("missing DOS header");

    dosHeader = (IMAGE_DOS_HEADER*)p;
    if (dosHeader->e_magic != IMAGE_DOS_SIGNATURE)
        throw InvalidImage("invalid DOS signature");

    // Skip to the NT headers. Note that we don't parse this section as
    // IMAGE_NT_HEADERS32/IMAGE_NT_HEADERS64 because we don't yet know if this
    // image is 32- or 64-bit. That information is in the first field of the
    // optional header.
    p += dosHeader->e_lfanew;

    //
    // Check the signature
    //
    if (p + sizeof(uint32_t) >= end)
        throw InvalidImage("missing PE signature");

    const uint32_t signature = *(uint32_t*)p;
    if (signature != *(const uint32_t*)"PE\0\0")
        throw InvalidImage("invalid PE signature");

    p += sizeof(signature);

    //
    // Parse the image file header
    //
    if (p + sizeof(IMAGE_FILE_HEADER) >= end)
        throw InvalidImage("missing IMAGE_FILE_HEADER");

    fileHeader = (IMAGE_FILE_HEADER*)p;

    p += sizeof(IMAGE_FILE_HEADER);

    //
    // The optional header is here. Parsing of this is delayed because it can
    // either be a 32- or 64-bit structure.
    //
    _optional = p;

    p += fileHeader->SizeOfOptionalHeader;

    //
    // Section headers. There are IMAGE_FILE_HEADER.NumberOfSections of these.
    //
    sectionHeaders = (IMAGE_SECTION_HEADER*)p;
}

/**
 * Patches the timestamp associated with a data directory.
 */
template<typename T>
void patchDataDirectory(const PEFile& pe, Patches& patches,
        const IMAGE_DATA_DIRECTORY* imageDataDirs,
        uint32_t entry, const char* name, uint32_t timestamp) {

    const IMAGE_DATA_DIRECTORY& imageDataDir = imageDataDirs[entry];

    // Doesn't exist? Nothing to patch.
    if (imageDataDir.VirtualAddress == 0)
        return;

    if (imageDataDir.Size < sizeof(T)) {
        // Note that we check if the size is less than our defined struct.
        // Microsoft is free to add elements to the end of the struct in future
        // versions as that still maintains ABI compatibility.
        throw InvalidImage("invalid IMAGE_DATA_DIRECTORY size");
    }

    const uint8_t* p = pe.translate(imageDataDir.VirtualAddress);
    if (!pe.isValidAddress(p))
        throw InvalidImage("invalid IMAGE_DATA_DIRECTORY offset");

    const T* dir = (const T*)p;

    if (dir->TimeDateStamp != 0)
        patches.add(&dir->TimeDateStamp, &timestamp, name);
}

/**
 * Patches the image based on the optional header type. The optional header can
 * be either 32- or 64-bit.
 */
template<typename T>
void patchOptionalHeader(const PEFile& pe,
        Patches& patches, uint32_t timestamp) {

    const T* optional = pe.optional<T>();

    patches.add(&optional->CheckSum, &timestamp,
            "OptionalHeader.CheckSum");

    const IMAGE_DATA_DIRECTORY* dataDirs = optional->DataDirectory;

    // Patch exports directory timestamp
    patchDataDirectory<IMAGE_EXPORT_DIRECTORY>(pe, patches,
            dataDirs, IMAGE_DIRECTORY_ENTRY_EXPORT,
            "IMAGE_EXPORT_DIRECTORY.TimeDateStamp", timestamp);

    // Patch resource directory timestamp
    patchDataDirectory<IMAGE_RESOURCE_DIRECTORY>(pe, patches,
            dataDirs, IMAGE_DIRECTORY_ENTRY_RESOURCE,
            "IMAGE_RESOURCE_DIRECTORY.TimeDateStamp", timestamp);
}

}

void patchImage(const char* imagePath, const char* pdbPath, bool dryRun) {
    MemMap image(imagePath);
    MemMap pdb(pdbPath);

    // Replacement for timestamps
    const uint32_t timestamp = 0;

    uint8_t* buf = (uint8_t*)image.buf();
    const size_t length = image.length();

    PEFile pe = PEFile(buf, length);

    Patches patches(buf);

    patches.add(&pe.fileHeader->TimeDateStamp, &timestamp,
            "IMAGE_FILE_HEADER.TimeDateStamp");

    switch (pe.magic()) {
        case IMAGE_NT_OPTIONAL_HDR32_MAGIC:
            // Patch as a PE32 file
            patchOptionalHeader<IMAGE_OPTIONAL_HEADER32>(pe,
                    patches, timestamp);
            break;

        case IMAGE_NT_OPTIONAL_HDR64_MAGIC:
            // Patch as a PE32+ file
            patchOptionalHeader<IMAGE_OPTIONAL_HEADER64>(pe,
                    patches, timestamp);
            break;

        default:
            throw InvalidImage("unsupported IMAGE_NT_HEADERS.OptionalHeader");
    }

    patches.apply(dryRun);
}
<|endoftext|>