<commit_before>#ifndef __EXTENSION_EXPLICIT_COUNTERSTRATEGY_HPP
#define __EXTENSION_EXPLICIT_COUNTERSTRATEGY_HPP

#include "gr1context.hpp"
#include <string>

/**
 * A class that computes an explicit state counterstrategy for an unrealizable specification
 */
template<class T> class XExtractExplicitCounterStrategy : public T {
protected:
    // New variables
    std::string outputFilename;

    // Inherited stuff used
    using T::mgr;
    using T::strategyDumpingData;
    using T::livenessGuarantees;
    using T::livenessAssumptions;
    using T::varVectorPre;
    using T::varVectorPost;
    using T::varCubePostOutput;
    using T::varCubePostInput;
    using T::varCubePreOutput;
    using T::varCubePreInput;
    using T::varCubePre;
    using T::varCubePost;
    using T::safetyEnv;
    using T::safetySys;
    using T::winningPositions;
    using T::initEnv;
    using T::initSys;
    using T::preVars;
    using T::postVars;
    using T::variableTypes;
    using T::variables;
    using T::variableNames;
    using T::realizable;
    using T::determinize;
    using T::postInputVars;
    using T::postOutputVars;
    using T::doesVariableInheritType;

    XExtractExplicitCounterStrategy<T>(std::list<std::string> &filenames) : T(filenames) {
        if (filenames.size()==1) {
            outputFilename = "";
        } else {
            outputFilename = filenames.front();
            filenames.pop_front();
        }
    }

public:

/**
 * @brief Compute and print out (to stdout) an explicit-state counter strategy that is winning for
 *        the environment. The output is compatible with the old JTLV output of LTLMoP.
 *        This function requires that the unrealizability of the specification has already been
 *        detected and that the variables "strategyDumpingData" and
 *        "winningPositions" have been filled by the synthesis algorithm with meaningful data.
 * @param outputStream - Where the strategy shall be printed to.
 */
void execute() {
        T::execute();
        if (!realizable) {
            if (outputFilename=="") {
                computeAndPrintExplicitStateStrategy(std::cout);
            } else {
                std::ofstream of(outputFilename.c_str());
                if (of.fail()) {
                    SlugsException ex(false);
                    ex << "Error: Could not open output file'" << outputFilename << "\n";
                    throw ex;
                }
                computeAndPrintExplicitStateStrategy(of);
                if (of.fail()) {
                    SlugsException ex(false);
                    ex << "Error: Writing to output file'" << outputFilename << "failed. \n";
                    throw ex;
                }
                of.close();
            }
        }
}

void computeAndPrintExplicitStateStrategy(std::ostream &outputStream) {

    // We don't want any reordering from this point onwards, as
    // the BDD manipulations from this point onwards are 'kind of simple'.
    mgr.setAutomaticOptimisation(false);

    // List of states in existance so far. The first map
    // maps from a BF node pointer (for the pre variable valuation) and a goal
    // to a state number. The vector then contains the concrete valuation.
    std::map<std::pair<size_t, std::pair<unsigned int, unsigned int> >, unsigned int > lookupTableForPastStates;
    std::vector<BF> bfsUsedInTheLookupTable;
    std::list<std::pair<size_t, std::pair<unsigned int, unsigned int> > > todoList;

    // Prepare positional strategies for the individual goals
    std::vector<std::vector<BF> > positionalStrategiesForTheIndividualGoals(livenessAssumptions.size());
    for (unsigned int i=0;i<livenessAssumptions.size();i++) {
        //BF casesCovered = mgr.constantFalse();
        std::vector<BF> strategy(livenessGuarantees.size()+1);
        for (unsigned int j=0;j<livenessGuarantees.size()+1;j++) {
            strategy[j] = mgr.constantFalse();
        }
        for (auto it = strategyDumpingData.begin();it!=strategyDumpingData.end();it++) {
            if (boost::get<0>(*it) == i) {
                //Have to cover each guarantee (since the winning strategy depends on which guarantee is being pursued)
                //Essentially, the choice of which guarantee to pursue can be thought of as a system "move".
                //The environment always to chooses that prevent the appropriate guarantee.
                strategy[boost::get<1>(*it)] |= boost::get<2>(*it).UnivAbstract(varCubePostOutput) & !(strategy[boost::get<1>(*it)].ExistAbstract(varCubePost));
            }
        }
        positionalStrategiesForTheIndividualGoals[i] = strategy;
    }

    // Prepare initial to-do list from the allowed initial states. Select a single initial input valuation.

    // TODO: Support for non-special-robotics semantics
    BF todoInit = (winningPositions & initEnv & initSys);
    while (!(todoInit.isFalse())) {
        BF concreteState = determinize(todoInit,preVars);

        //find which liveness guarantee is being prevented (finds the first liveness in order specified)
        // Note by Ruediger here: Removed "!livenessGuarantees[j]" as condition as it is non-positional
        unsigned int found_j_index = 0;
        for (unsigned int j=0;j<livenessGuarantees.size();j++) {
            if (!(concreteState & positionalStrategiesForTheIndividualGoals[0][j]).isFalse()) {
                found_j_index = j;
                break;
            }
        }

        std::pair<size_t, std::pair<unsigned int, unsigned int> > lookup = std::pair<size_t, std::pair<unsigned int, unsigned int> >(concreteState.getHashCode(),std::pair<unsigned int, unsigned int>(0,found_j_index));
        lookupTableForPastStates[lookup] = bfsUsedInTheLookupTable.size();
        bfsUsedInTheLookupTable.push_back(concreteState);
        //from now on use the same initial input valuation (but consider all other initial output valuations)
        todoInit &= !concreteState;
        todoList.push_back(lookup);
    }

    // Extract strategy
    while (todoList.size()>0) {
        std::pair<size_t, std::pair<unsigned int, unsigned int> > current = todoList.front();
        todoList.pop_front();
        unsigned int stateNum = lookupTableForPastStates[current];
        BF currentPossibilities = bfsUsedInTheLookupTable[stateNum];
        // Print state information
        outputStream << "State " << stateNum << " with rank (" << current.second.first << "," << current.second.second << ") -> <";
        bool first = true;
        for (unsigned int i=0;i<variables.size();i++) {
            if (doesVariableInheritType(i,Pre)) {
                if (first) {
                    first = false;
                } else {
                    outputStream << ", ";
                }
                outputStream << variableNames[i] << ":";
                outputStream << (((currentPossibilities & variables[i]).isFalse())?"0":"1");
            }
        }

        outputStream << ">\n\tWith successors : ";
        first = true;

        // Can we enforce a deadlock?
        BF deadlockInput = (currentPossibilities & safetyEnv & !safetySys).UnivAbstract(varCubePostOutput);
        if (deadlockInput!=mgr.constantFalse()) {
            addDeadlocked(deadlockInput, current, bfsUsedInTheLookupTable,  lookupTableForPastStates, outputStream);
        } else {

            // No deadlock in sight -> Jump to a different liveness guarantee if necessary.
            while ((currentPossibilities & positionalStrategiesForTheIndividualGoals[current.second.first][current.second.second])==mgr.constantFalse()) current.second.second = (current.second.second + 1) % livenessGuarantees.size();
            currentPossibilities &= positionalStrategiesForTheIndividualGoals[current.second.first][current.second.second];
            assert(currentPossibilities != mgr.constantFalse());
            BF remainingTransitions = currentPossibilities;

            // Choose one next input and stick to it!
            remainingTransitions = determinize(remainingTransitions,postInputVars);

            // Switching goals
            while (!(remainingTransitions & safetySys).isFalse()) {

                BF safeTransition = remainingTransitions & safetySys;
                BF newCombination = determinize(safeTransition, postOutputVars);

                // Jump as much forward  in the liveness assumption list as possible ("stuttering avoidance")
                unsigned int nextLivenessAssumption = current.second.first;
                bool firstTry = true;
                while (((nextLivenessAssumption != current.second.first) | firstTry) && !((livenessAssumptions[nextLivenessAssumption] & newCombination).isFalse())) {
                    nextLivenessAssumption  = (nextLivenessAssumption + 1) % livenessAssumptions.size();
                    firstTry = false;
                }

                //Mark which input has been captured by this case. Use the same input for other successors
                remainingTransitions &= !newCombination;

                // We don't need the pre information from the point onwards anymore.
                newCombination = newCombination.ExistAbstract(varCubePre).SwapVariables(varVectorPre,varVectorPost);

                unsigned int tn;

                std::pair<size_t, std::pair<unsigned int, unsigned int> > target;

                target = std::pair<size_t, std::pair<unsigned int, unsigned int> >(newCombination.getHashCode(),std::pair<unsigned int, unsigned int>(nextLivenessAssumption, current.second.second));

                if (lookupTableForPastStates.count(target)==0) {
                    tn = lookupTableForPastStates[target] = bfsUsedInTheLookupTable.size();
                    bfsUsedInTheLookupTable.push_back(newCombination);
                    todoList.push_back(target);
                } else {
                    tn = lookupTableForPastStates[target];
                }

                // Print
                if (first) {
                    first = false;
                } else {
                    outputStream << ", ";
                }
                outputStream << tn;

            }
        }

        outputStream << "\n";
    }
    }

    //This function adds a new successor-less "state" that captures the deadlock-causing input values
    //The outputvalues are omitted (indeed, no valuation exists that satisfies the system safeties)
    //Format compatible with JTLV counterstrategy

    void addDeadlocked(BF targetPositionCandidateSet, std::pair<size_t, std::pair<unsigned int, unsigned int> > current, std::vector<BF> &bfsUsedInTheLookupTable, std::map<std::pair<size_t, std::pair<unsigned int, unsigned int> >, unsigned int > &lookupTableForPastStates, std::ostream &outputStream) {

    BF newCombination = determinize(targetPositionCandidateSet, postVars) ;

    newCombination  = newCombination.ExistAbstract(varCubePreOutput).SwapVariables(varVectorPre,varVectorPost);

    std::pair<size_t, std::pair<unsigned int, unsigned int> > target = std::pair<size_t, std::pair<unsigned int, unsigned int> >(newCombination.getHashCode(),std::pair<unsigned int, unsigned int>(current.second.first, current.second.second));
    unsigned int tn;

    if (lookupTableForPastStates.count(target)==0) {
        tn = lookupTableForPastStates[target] = bfsUsedInTheLookupTable.size();
        bfsUsedInTheLookupTable.push_back(newCombination);
        outputStream << tn << "\n";

        //Note that since we are printing here, we usually end up with the states being printed out of order.
        //TOTO: print in order
        outputStream << "State " << tn << " with rank (" << current.second.first << "," << current.second.second << ") -> <";
        bool first = true;
        for (unsigned int i=0;i<variables.size();i++) {
            if (doesVariableInheritType(i,PreInput)) {
                if (first) {
                    first = false;
                } else {
                    outputStream << ", ";
                }
                outputStream << variableNames[i] << ":";
                outputStream << (((newCombination & variables[i]).isFalse())?"0":"1");
            }
        }
        outputStream << ">\n\tWith no successors.";

    } else {
        tn = lookupTableForPastStates[target];
        outputStream << tn;
    }

}

    static GR1Context* makeInstance(std::list<std::string> &filenames) {
        return new XExtractExplicitCounterStrategy<T>(filenames);
    }
};

#endif

<commit_msg>added bugfix to deadlock extraction from 8fed8cc6d39bdf9d3e66d8188eddc66e95a8bc74<commit_after>#ifndef __EXTENSION_EXPLICIT_COUNTERSTRATEGY_HPP
#define __EXTENSION_EXPLICIT_COUNTERSTRATEGY_HPP

#include "gr1context.hpp"
#include <string>

/**
 * A class that computes an explicit state counterstrategy for an unrealizable specification
 */
template<class T> class XExtractExplicitCounterStrategy : public T {
protected:
    // New variables
    std::string outputFilename;

    // Inherited stuff used
    using T::mgr;
    using T::strategyDumpingData;
    using T::livenessGuarantees;
    using T::livenessAssumptions;
    using T::varVectorPre;
    using T::varVectorPost;
    using T::varCubePostOutput;
    using T::varCubePostInput;
    using T::varCubePreOutput;
    using T::varCubePreInput;
    using T::varCubePre;
    using T::varCubePost;
    using T::safetyEnv;
    using T::safetySys;
    using T::winningPositions;
    using T::initEnv;
    using T::initSys;
    using T::preVars;
    using T::postVars;
    using T::variableTypes;
    using T::variables;
    using T::variableNames;
    using T::realizable;
    using T::determinize;
    using T::postInputVars;
    using T::postOutputVars;
    using T::doesVariableInheritType;

    XExtractExplicitCounterStrategy<T>(std::list<std::string> &filenames) : T(filenames) {
        if (filenames.size()==1) {
            outputFilename = "";
        } else {
            outputFilename = filenames.front();
            filenames.pop_front();
        }
    }

public:

/**
 * @brief Compute and print out (to stdout) an explicit-state counter strategy that is winning for
 *        the environment. The output is compatible with the old JTLV output of LTLMoP.
 *        This function requires that the unrealizability of the specification has already been
 *        detected and that the variables "strategyDumpingData" and
 *        "winningPositions" have been filled by the synthesis algorithm with meaningful data.
 * @param outputStream - Where the strategy shall be printed to.
 */
void execute() {
        T::execute();
        if (!realizable) {
            if (outputFilename=="") {
                computeAndPrintExplicitStateStrategy(std::cout);
            } else {
                std::ofstream of(outputFilename.c_str());
                if (of.fail()) {
                    SlugsException ex(false);
                    ex << "Error: Could not open output file'" << outputFilename << "\n";
                    throw ex;
                }
                computeAndPrintExplicitStateStrategy(of);
                if (of.fail()) {
                    SlugsException ex(false);
                    ex << "Error: Writing to output file'" << outputFilename << "failed. \n";
                    throw ex;
                }
                of.close();
            }
        }
}

void computeAndPrintExplicitStateStrategy(std::ostream &outputStream) {

    // We don't want any reordering from this point onwards, as
    // the BDD manipulations from this point onwards are 'kind of simple'.
    mgr.setAutomaticOptimisation(false);

    // List of states in existance so far. The first map
    // maps from a BF node pointer (for the pre variable valuation) and a goal
    // to a state number. The vector then contains the concrete valuation.
    std::map<std::pair<size_t, std::pair<unsigned int, unsigned int> >, unsigned int > lookupTableForPastStates;
    std::vector<BF> bfsUsedInTheLookupTable;
    std::list<std::pair<size_t, std::pair<unsigned int, unsigned int> > > todoList;

    // Prepare positional strategies for the individual goals
    std::vector<std::vector<BF> > positionalStrategiesForTheIndividualGoals(livenessAssumptions.size());
    for (unsigned int i=0;i<livenessAssumptions.size();i++) {
        //BF casesCovered = mgr.constantFalse();
        std::vector<BF> strategy(livenessGuarantees.size()+1);
        for (unsigned int j=0;j<livenessGuarantees.size()+1;j++) {
            strategy[j] = mgr.constantFalse();
        }
        for (auto it = strategyDumpingData.begin();it!=strategyDumpingData.end();it++) {
            if (boost::get<0>(*it) == i) {
                //Have to cover each guarantee (since the winning strategy depends on which guarantee is being pursued)
                //Essentially, the choice of which guarantee to pursue can be thought of as a system "move".
                //The environment always to chooses that prevent the appropriate guarantee.
                strategy[boost::get<1>(*it)] |= boost::get<2>(*it).UnivAbstract(varCubePostOutput) & !(strategy[boost::get<1>(*it)].ExistAbstract(varCubePost));
            }
        }
        positionalStrategiesForTheIndividualGoals[i] = strategy;
    }

    // Prepare initial to-do list from the allowed initial states. Select a single initial input valuation.

    // TODO: Support for non-special-robotics semantics
    BF todoInit = (winningPositions & initEnv & initSys);
    while (!(todoInit.isFalse())) {
        BF concreteState = determinize(todoInit,preVars);

        //find which liveness guarantee is being prevented (finds the first liveness in order specified)
        // Note by Ruediger here: Removed "!livenessGuarantees[j]" as condition as it is non-positional
        unsigned int found_j_index = 0;
        for (unsigned int j=0;j<livenessGuarantees.size();j++) {
            if (!(concreteState & positionalStrategiesForTheIndividualGoals[0][j]).isFalse()) {
                found_j_index = j;
                break;
            }
        }

        std::pair<size_t, std::pair<unsigned int, unsigned int> > lookup = std::pair<size_t, std::pair<unsigned int, unsigned int> >(concreteState.getHashCode(),std::pair<unsigned int, unsigned int>(0,found_j_index));
        lookupTableForPastStates[lookup] = bfsUsedInTheLookupTable.size();
        bfsUsedInTheLookupTable.push_back(concreteState);
        //from now on use the same initial input valuation (but consider all other initial output valuations)
        todoInit &= !concreteState;
        todoList.push_back(lookup);
    }

    // Extract strategy
    while (todoList.size()>0) {
        std::pair<size_t, std::pair<unsigned int, unsigned int> > current = todoList.front();
        todoList.pop_front();
        unsigned int stateNum = lookupTableForPastStates[current];
        BF currentPossibilities = bfsUsedInTheLookupTable[stateNum];
        // Print state information
        outputStream << "State " << stateNum << " with rank (" << current.second.first << "," << current.second.second << ") -> <";
        bool first = true;
        for (unsigned int i=0;i<variables.size();i++) {
            if (doesVariableInheritType(i,Pre)) {
                if (first) {
                    first = false;
                } else {
                    outputStream << ", ";
                }
                outputStream << variableNames[i] << ":";
                outputStream << (((currentPossibilities & variables[i]).isFalse())?"0":"1");
            }
        }

        outputStream << ">\n\tWith successors : ";
        first = true;

        // Can we enforce a deadlock?
        BF deadlockInput = (currentPossibilities & safetyEnv & !safetySys).UnivAbstract(varCubePostOutput);
        if (deadlockInput!=mgr.constantFalse()) {
            addDeadlocked(deadlockInput, current, bfsUsedInTheLookupTable,  lookupTableForPastStates, outputStream);
        } else {

            // No deadlock in sight -> Jump to a different liveness guarantee if necessary.
            while ((currentPossibilities & positionalStrategiesForTheIndividualGoals[current.second.first][current.second.second])==mgr.constantFalse()) current.second.second = (current.second.second + 1) % livenessGuarantees.size();
            currentPossibilities &= positionalStrategiesForTheIndividualGoals[current.second.first][current.second.second];
            assert(currentPossibilities != mgr.constantFalse());
            BF remainingTransitions = currentPossibilities;

            // Choose one next input and stick to it!
            remainingTransitions = determinize(remainingTransitions,postInputVars);

            // Switching goals
            while (!(remainingTransitions & safetySys).isFalse()) {

                BF safeTransition = remainingTransitions & safetySys;
                BF newCombination = determinize(safeTransition, postOutputVars);

                // Jump as much forward  in the liveness assumption list as possible ("stuttering avoidance")
                unsigned int nextLivenessAssumption = current.second.first;
                bool firstTry = true;
                while (((nextLivenessAssumption != current.second.first) | firstTry) && !((livenessAssumptions[nextLivenessAssumption] & newCombination).isFalse())) {
                    nextLivenessAssumption  = (nextLivenessAssumption + 1) % livenessAssumptions.size();
                    firstTry = false;
                }

                //Mark which input has been captured by this case. Use the same input for other successors
                remainingTransitions &= !newCombination;

                // We don't need the pre information from the point onwards anymore.
                newCombination = newCombination.ExistAbstract(varCubePre).SwapVariables(varVectorPre,varVectorPost);

                unsigned int tn;

                std::pair<size_t, std::pair<unsigned int, unsigned int> > target;

                target = std::pair<size_t, std::pair<unsigned int, unsigned int> >(newCombination.getHashCode(),std::pair<unsigned int, unsigned int>(nextLivenessAssumption, current.second.second));

                if (lookupTableForPastStates.count(target)==0) {
                    tn = lookupTableForPastStates[target] = bfsUsedInTheLookupTable.size();
                    bfsUsedInTheLookupTable.push_back(newCombination);
                    todoList.push_back(target);
                } else {
                    tn = lookupTableForPastStates[target];
                }

                // Print
                if (first) {
                    first = false;
                } else {
                    outputStream << ", ";
                }
                outputStream << tn;

            }
        }

        outputStream << "\n";
    }
    }

    //This function adds a new successor-less "state" that captures the deadlock-causing input values
    //The outputvalues are omitted (indeed, no valuation exists that satisfies the system safeties)
    //Format compatible with JTLV counterstrategy

    void addDeadlocked(BF targetPositionCandidateSet, std::pair<size_t, std::pair<unsigned int, unsigned int> > current, std::vector<BF> &bfsUsedInTheLookupTable, std::map<std::pair<size_t, std::pair<unsigned int, unsigned int> >, unsigned int > &lookupTableForPastStates, std::ostream &outputStream) {

    BF newCombination = determinize(targetPositionCandidateSet, postVars) ;

    newCombination  = (newCombination.ExistAbstract(varCubePostOutput).ExistAbstract(varCubePre)).SwapVariables(varVectorPre,varVectorPost);

    std::pair<size_t, std::pair<unsigned int, unsigned int> > target = std::pair<size_t, std::pair<unsigned int, unsigned int> >(newCombination.getHashCode(),std::pair<unsigned int, unsigned int>(current.second.first, current.second.second));
    unsigned int tn;

    if (lookupTableForPastStates.count(target)==0) {
        tn = lookupTableForPastStates[target] = bfsUsedInTheLookupTable.size();
        bfsUsedInTheLookupTable.push_back(newCombination);
        outputStream << tn << "\n";

        //Note that since we are printing here, we usually end up with the states being printed out of order.
        //TOTO: print in order
        outputStream << "State " << tn << " with rank (" << current.second.first << "," << current.second.second << ") -> <";
        bool first = true;
        for (unsigned int i=0;i<variables.size();i++) {
            if (doesVariableInheritType(i,PreInput)) {
                if (first) {
                    first = false;
                } else {
                    outputStream << ", ";
                }
                outputStream << variableNames[i] << ":";
                outputStream << (((newCombination & variables[i]).isFalse())?"0":"1");
            }
        }
        outputStream << ">\n\tWith no successors.";

    } else {
        tn = lookupTableForPastStates[target];
        outputStream << tn;
    }

}

    static GR1Context* makeInstance(std::list<std::string> &filenames) {
        return new XExtractExplicitCounterStrategy<T>(filenames);
    }
};

#endif

<|endoftext|>


<commit_before>/*------------------------------------------------------------------------
* (The MIT License)
*
* Copyright (c) 2008-2011 Rhomobile, Inc.
*
* 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.
*
* http://rhomobile.com
*------------------------------------------------------------------------*/

#include "statistic/RhoProfiler.h"

#include "rhodes/JNIRhodes.h"

#include "rhodes/jni/com_rhomobile_rhodes_Logger.h"

#undef DEFAULT_LOGCATEGORY
#define DEFAULT_LOGCATEGORY "Logger"

RHO_GLOBAL void JNICALL Java_com_rhomobile_rhodes_Logger_E
  (JNIEnv *env, jclass, jstring tag, jstring msg)
{
    RAWLOGC_ERROR(rho_cast<std::string>(env, tag).c_str(), rho_cast<std::string>(env, msg).c_str());
}

RHO_GLOBAL void JNICALL Java_com_rhomobile_rhodes_Logger_W
  (JNIEnv *env, jclass, jstring tag, jstring msg)
{
    RAWLOGC_ERROR(rho_cast<std::string>(env, tag).c_str(), rho_cast<std::string>(env, msg).c_str());
}

RHO_GLOBAL void JNICALL Java_com_rhomobile_rhodes_Logger_I
  (JNIEnv *env, jclass, jstring tag, jstring msg)
{
    RAWLOGC_INFO(rho_cast<std::string>(env, tag).c_str(), rho_cast<std::string>(env, msg).c_str());
}

RHO_GLOBAL void JNICALL Java_com_rhomobile_rhodes_Logger_D
  (JNIEnv *env, jclass, jstring tag, jstring msg)
{
    RAWLOGC_INFO(rho_cast<std::string>(env, tag).c_str(), rho_cast<std::string>(env, msg).c_str());
}

RHO_GLOBAL void JNICALL Java_com_rhomobile_rhodes_Logger_T
  (JNIEnv *env, jclass, jstring tag, jstring msg)
{
    RAWTRACEC(rho_cast<std::string>(env, tag).c_str(), rho_cast<std::string>(env, msg).c_str());
}

RHO_GLOBAL void JNICALL Java_com_rhomobile_rhodes_Logger_profStart
  (JNIEnv *env, jclass, jstring tag)
{
    PROF_START(rho_cast<std::string>(env, tag).c_str());
}

RHO_GLOBAL void JNICALL Java_com_rhomobile_rhodes_Logger_profStop
  (JNIEnv *env, jclass, jstring tag)
{
    PROF_STOP(rho_cast<std::string>(env, tag).c_str());
}

<commit_msg>Android: Fix logger level routines<commit_after>/*------------------------------------------------------------------------
* (The MIT License)
*
* Copyright (c) 2008-2011 Rhomobile, Inc.
*
* 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.
*
* http://rhomobile.com
*------------------------------------------------------------------------*/

#include "statistic/RhoProfiler.h"

#include "rhodes/JNIRhodes.h"

#include "rhodes/jni/com_rhomobile_rhodes_Logger.h"

#undef DEFAULT_LOGCATEGORY
#define DEFAULT_LOGCATEGORY "Logger"

RHO_GLOBAL void JNICALL Java_com_rhomobile_rhodes_Logger_E
  (JNIEnv *env, jclass, jstring tag, jstring msg)
{
    RAWLOGC_ERROR(rho_cast<std::string>(env, tag).c_str(), rho_cast<std::string>(env, msg).c_str());
}

RHO_GLOBAL void JNICALL Java_com_rhomobile_rhodes_Logger_W
  (JNIEnv *env, jclass, jstring tag, jstring msg)
{
    RAWLOGC_WARNING(rho_cast<std::string>(env, tag).c_str(), rho_cast<std::string>(env, msg).c_str());
}

RHO_GLOBAL void JNICALL Java_com_rhomobile_rhodes_Logger_I
  (JNIEnv *env, jclass, jstring tag, jstring msg)
{
    RAWLOGC_INFO(rho_cast<std::string>(env, tag).c_str(), rho_cast<std::string>(env, msg).c_str());
}

RHO_GLOBAL void JNICALL Java_com_rhomobile_rhodes_Logger_D
  (JNIEnv *env, jclass, jstring tag, jstring msg)
{
#ifdef _DEBUG
    RAWLOGC_INFO(rho_cast<std::string>(env, tag).c_str(), rho_cast<std::string>(env, msg).c_str());
#endif
}

RHO_GLOBAL void JNICALL Java_com_rhomobile_rhodes_Logger_T
  (JNIEnv *env, jclass, jstring tag, jstring msg)
{
    RAWTRACEC(rho_cast<std::string>(env, tag).c_str(), rho_cast<std::string>(env, msg).c_str());
}

RHO_GLOBAL void JNICALL Java_com_rhomobile_rhodes_Logger_profStart
  (JNIEnv *env, jclass, jstring tag)
{
    PROF_START(rho_cast<std::string>(env, tag).c_str());
}

RHO_GLOBAL void JNICALL Java_com_rhomobile_rhodes_Logger_profStop
  (JNIEnv *env, jclass, jstring tag)
{
    PROF_STOP(rho_cast<std::string>(env, tag).c_str());
}

<|endoftext|>


<commit_before>/*******************************************************************************

Licensed to the OpenCOR team under one or more contributor license agreements.
See the NOTICE.txt file distributed with this work for additional information
regarding copyright ownership. The OpenCOR team licenses this file to you under
the Apache License, Version 2.0 (the "License"); you may not use this file
except in compliance with the License. You may obtain a copy of the License at

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

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

*******************************************************************************/

//==============================================================================
// File
//==============================================================================

#include "coreutils.h"
#include "file.h"

//==============================================================================

#include <QCryptographicHash>
#include <QFileInfo>
#include <QTextStream>

//==============================================================================

namespace OpenCOR {
namespace Core {

//==============================================================================

File::File(const QString &pFileName) :
    mFileName(nativeCanonicalFileName(pFileName)),
    mSha1(sha1()),
    mModified(false)
{
}

//==============================================================================

QString File::fileName() const
{
    // Return the file name of the file

    return mFileName;
}

//==============================================================================

void File::setFileName(const QString &pFileName)
{
    // Set the new file name of the file

    mFileName = pFileName;
}

//==============================================================================

File::Status File::check()
{
    // Retrieve the SHA-1 value of our file and compare it to its stored value

    QString crtSha1 = sha1();

    if (!crtSha1.compare(mSha1)) {
        // The SHA-1 values are the same, so...

        return File::Unchanged;
    } else if (crtSha1.isEmpty()) {
        // The SHA-1 value of our file is now empty, which means that our file
        // has been deleted, so update our stored value and let people know
        // about it

        mSha1 = crtSha1;

        return File::Deleted;
    } else {
        // The SHA-1 value of our file is different from our stored value, so
        // update it and let people know that our file has changed

        mSha1 = crtSha1;

        return File::Changed;
    }
}

//==============================================================================

QString File::sha1() const
{
    // Compute the SHA-1 value for the file, if it still exists

    if (QFileInfo(mFileName).exists()) {
        // The file still exists, so we can go ahead and compute its SHA-1 value

        QFile file(mFileName);

        file.open(QIODevice::ReadOnly);

        QString res = QCryptographicHash::hash(QTextStream(&file).readAll().toUtf8(),
                                               QCryptographicHash::Sha1).toHex();

        file.close();

        return res;
    } else {
        // The file doesn't exist anymore, so...

        return QString();
    }
}

//==============================================================================

bool File::isModified() const
{
    // Return whether the file has been modified

    return mModified;
}

//==============================================================================

void File::setModified(const bool &pModified)
{
    // Set the modified status of the file

    mModified = pModified;
}

//==============================================================================

}   // namespace Core
}   // namespace OpenCOR

//==============================================================================
// End of file
//==============================================================================
<commit_msg>Minor improvement to our File class. Also some minor cleaning up.<commit_after>/*******************************************************************************

Licensed to the OpenCOR team under one or more contributor license agreements.
See the NOTICE.txt file distributed with this work for additional information
regarding copyright ownership. The OpenCOR team licenses this file to you under
the Apache License, Version 2.0 (the "License"); you may not use this file
except in compliance with the License. You may obtain a copy of the License at

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

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

*******************************************************************************/

//==============================================================================
// File
//==============================================================================

#include "coreutils.h"
#include "file.h"

//==============================================================================

#include <QCryptographicHash>
#include <QFileInfo>
#include <QTextStream>

//==============================================================================

namespace OpenCOR {
namespace Core {

//==============================================================================

File::File(const QString &pFileName) :
    mFileName(nativeCanonicalFileName(pFileName)),
    mSha1(sha1()),
    mModified(false)
{
}

//==============================================================================

QString File::fileName() const
{
    // Return the file name of the file

    return mFileName;
}

//==============================================================================

void File::setFileName(const QString &pFileName)
{
    // Set the new file name of the file

    mFileName = pFileName;
}

//==============================================================================

File::Status File::check()
{
    // Retrieve the SHA-1 value of our file and compare it to its stored value

    QString newSha1 = sha1();

    if (!newSha1.compare(mSha1)) {
        // The SHA-1 values are the same, so...

        return File::Unchanged;
    } else {
        // We have a new SHA-1 value, so keep track of it

        mSha1 = newSha1;

        if (newSha1.isEmpty())
            // The SHA-1 value of our file is now empty, which means that our
            // file has been deleted

            return File::Deleted;
        else
            // The SHA-1 value of our file is different from our stored value,
            // which means that our file has changed

            return File::Changed;
    }
}

//==============================================================================

QString File::sha1() const
{
    // Compute the SHA-1 value for the file, if it still exists

    if (QFileInfo(mFileName).exists()) {
        // The file still exists, so we can go ahead and compute its SHA-1 value

        QFile file(mFileName);

        file.open(QIODevice::ReadOnly);

        QString res = QCryptographicHash::hash(QTextStream(&file).readAll().toUtf8(),
                                               QCryptographicHash::Sha1).toHex();

        file.close();

        return res;
    } else {
        // The file doesn't exist anymore, so...

        return QString();
    }
}

//==============================================================================

bool File::isModified() const
{
    // Return whether the file has been modified

    return mModified;
}

//==============================================================================

void File::setModified(const bool &pModified)
{
    // Set the modified status of the file

    mModified = pModified;

    // Also update our SHA-1 value in case the physical state of our file has
    // changed

    mSha1 = sha1();
}

//==============================================================================

}   // namespace Core
}   // namespace OpenCOR

//==============================================================================
// End of file
//==============================================================================
<|endoftext|>


<commit_before>// Copyright 2003-2016 Nicholas J. Kain <njkain at gmail dot com>
// SPDX-License-Identifier: MIT
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <time.h>
#include <sys/time.h>
#include <string.h>
#include <limits.h>
#include <assert.h>
#include <nk/scopeguard.hpp>
extern "C" {
#include "nk/log.h"
#include "nk/pspawn.h"
#include "nk/privs.h"
#include "nk/io.h"
}
#include "ncron.hpp"
#include "sched.hpp"

extern char **environ;

#define COUNT_THRESH 500 /* Arbitrary and untested */

/*
 * returns -1 if below range
 * returns 0 if within range
 * returns 1 if above range
 */
static inline int compare_range(const std::pair<int,int> &r,
                                int v, int wildcard)
{
    if (r.first == wildcard && r.second == wildcard)
        return 0;
    if (r.first == wildcard) {
        if (v < r.second)
            return -1;
        if (v > r.second)
            return 1;
        return 0;
    }
    if (r.second == wildcard) {
        if (v < r.first)
            return -1;
        if (v > r.first)
            return 1;
        return 0;
    }
    if (v < r.first)
        return -1;
    if (v > r.second)
        return 1;
    return 0;
}

/*
 * 1 if range is valid, 0 if not; assumes for constraint and range: (x,y) | x<y
 */
static inline int is_range_valid(const std::pair<int,int> &constraint,
                                 const std::pair<int,int> &range,
                                 int wildcard)
{
    if (range.first < constraint.first && range.first != wildcard)
        return 0;
    if (range.second > constraint.second && range.second != wildcard)
        return 0;
    return 1;
}

/*
 * This fn does the actual constraint.  If our tested value falls within
 * constraint ranges, then nothing is done.  Otherwise, the 'nearest'
 * constraint range that is above the current value is chosen.  If no such
 * range exists, then the next higher unit is incremented by one and the
 * smallest extant valid value is chosen for the constrained unit.
 */
static int compare_list_range_v(const cronentry_t::cst_list &list, int *unit,
                                int *nextunit, const std::pair<int,int> &valid,
                                int wildcard)
{
    int t, smallunit = INT_MAX, dist = INT_MAX, tinyunit = INT_MAX;

    if (list.empty() || !unit || !nextunit)
        return 0;

    /* find the range least distant from our target value */
    for (const auto &i: list) {
        if (i.first < tinyunit)
            tinyunit = i.first;
        if (is_range_valid(valid, i, wildcard)) {
            switch (compare_range(i, *unit, wildcard)) {
                case 1: /* range below our value, avoid if possible */
                    t = *unit - i.second; /* t > 0 */
                    if (t < abs(dist) && dist > 0)
                        dist = t;
                    smallunit = i.first;
                    break;
                default: /* bingo, our value is within a range */
                    return 0;
                case -1: /* range above our value, favor */
                    t = *unit - i.first; /* implicitly, t < 0 */
                    if (abs(t) < abs(dist) || dist > 0)
                        dist = t;
                    smallunit = i.first;
                    break;
            }
        }
    }

    /* All of our constraints are invalid, so act as if all are wildcard. */
    if (tinyunit == INT_MAX)
        return 0;

    if (dist > 0) {
        (*nextunit)++;
        *unit = tinyunit;
    }
    if (dist < 0)
        *unit = smallunit;
    if (dist == 0)
        return 0;
    return 1;
}

/* same as above, but no check on range validity */
static int compare_list_range(const cronentry_t::cst_list &list, int *unit,
                              int *nextunit, int wildcard)
{
    int t, smallunit = INT_MAX, dist = INT_MAX, tinyunit = INT_MAX;

    if (list.empty() || !unit || !nextunit)
        return 0;

    /* find the range least distant from our target value */
    for (const auto &i: list) {
        if (i.first < tinyunit) tinyunit = i.first;
        switch (compare_range(i, *unit, wildcard)) {
            case 1: /* range below our value, avoid if possible */
                t = *unit - i.second; /* t > 0 */
                if (dist > 0 && t < dist)
                    dist = t;
                smallunit = i.first;
                break;
            default: /* bingo, our value is within a range */
                return 0;
            case -1: /* range above our value, favor */
                t = *unit - i.first; /* implicitly, t < 0 */
                if (dist > 0 || t > dist)
                    dist = t;
                smallunit = i.first;
                break;
        }
    }

    if (dist > 0) {
        (*nextunit)++;
        *unit = tinyunit;
    }
    if (dist < 0)
        *unit = smallunit;
    if (dist == 0)
        return 0;
    return 1;
}

static int compare_wday_range(const cronentry_t::cst_list &list, int *unit,
                              int *nextunit)
{
    int dist = INT_MAX, t;
    if (list.empty() || !unit || !nextunit)
        return 0;

    /* find the range least distant from our target value */
    for (const auto &i: list) {
        switch (compare_range(i, *unit, 0)) {
            case 1:
                t = *unit - i.second;
                if (dist > 0 && t < dist)
                    dist = t;
                break;
            case 0:
                return 0;
            case -1:
                t = *unit - i.first;
                if (dist > 0 || t > dist)
                    dist = t;
                break;
        }
    }

    if (dist < 0) {
        nextunit -= dist;
        return 1;
    }

    (*nextunit)++;
    return 1;
}

static std::pair<int,int> valid_day_of_month(int month, int year)
{
    auto ret = std::make_pair<int,int>(1, 31);
    switch (month) {
    case 2: /* we follow the gregorian calendar */
        if (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0))
            ret.second = 29;
        else
            ret.second = 28;
        break;
    case 4: case 6: case 9: case 11: ret.second = 30; default: break;
    }
    return ret;
}

/* entry is obvious, stime is the time we're constraining
 * returns a time value that has been appropriately constrained */
static time_t constrain_time(const cronentry_t &entry, time_t stime)
{
    struct tm *rtime;
    time_t t;
    int count = 0;

    rtime = localtime(&stime);

    for (; count < COUNT_THRESH; count++) {
        t = mktime(rtime);
        rtime = localtime(&t);

        if (compare_list_range(entry.minute, &(rtime->tm_min),
                    &(rtime->tm_hour), 60))
            continue;
        if (compare_list_range(entry.hour, &(rtime->tm_hour),
                    &(rtime->tm_mday), 24))
            continue;
        if (compare_list_range_v(entry.day, &(rtime->tm_mday),
                    &(rtime->tm_mon), valid_day_of_month(rtime->tm_mon,
                        rtime->tm_year), 0))
            continue;
        if (compare_wday_range(entry.weekday, &(rtime->tm_wday),
                    &(rtime->tm_mday)))
            continue;
        if (compare_list_range(entry.month, &(rtime->tm_mon),
                    &(rtime->tm_year), 0))
            continue;
        return mktime(rtime);
    }
    /* Failed to find a suitable time. */
    return 0;
}

/* Used when jobs without exectimes are first loaded. */
void set_initial_exectime(cronentry_t &entry)
{
    struct timespec ts;
    clock_or_die(&ts);
    time_t ttm = constrain_time(entry, ts.tv_sec);
    time_t ttd = ttm - entry.lasttime;
    if (ttd < entry.interval) {
        ttm += entry.interval - ttd;
        ttm = constrain_time(entry, ttm);
    }
    entry.exectime = ttm;
}

/* stupidly advances to next time of execution; performs constraint.  */
void cronentry_t::set_next_time()
{
    struct timespec ts;
    clock_or_die(&ts);
    auto etime = constrain_time(*this, ts.tv_sec + interval);
    exectime = etime > ts.tv_sec ? etime : 0;
}

void save_stack(const std::string &file,
                const std::vector<StackItem> &stack,
                const std::vector<StackItem> &deadstack)
{
    char buf[MAXLINE];

    auto f = fopen(file.c_str(), "w");
    if (!f) {
        log_line("%s: failed to open history file %s for write", __func__, file.c_str());
        std::exit(EXIT_FAILURE);
    }
    SCOPE_EXIT{ fclose(f); };

    auto do_save = [&buf, &f](const std::vector<StackItem> &s) -> bool {
        for (auto &i: s) {
            auto snlen = snprintf(buf, sizeof buf, "%u=%li:%u|%lu\n", i.ce->id,
                                  i.ce->exectime, i.ce->numruns, i.ce->lasttime);
            if (snlen < 0 || static_cast<std::size_t>(snlen) > sizeof buf) {
                log_line("save_stack: Would truncate history entry for job %u; skipping.", i.ce->id);
                continue;
            }
            auto bsize = strlen(buf);
            while (!fwrite(buf, bsize, 1, f)) {
                if (ferror(f))
                    return false;
            }
        }
        return true;
    };
    if (!do_save(stack)) return;
    if (!do_save(deadstack)) return;
}

void cronentry_t::exec(const struct timespec &ts)
{
    pid_t pid;
    posix_spawn_file_actions_t fdact;
    posix_spawn_file_actions_init(&fdact);
    SCOPE_EXIT{ posix_spawn_file_actions_destroy(&fdact); };
    if (int ret = nk_pspawn(&pid, command.c_str(), &fdact, nullptr, args.c_str(), environ)) {
        log_line("posix_spawn failed for '%s': %s\n", command.c_str(), strerror(ret));
        return;
    }
    ++numruns;
    lasttime = ts.tv_sec;
    set_next_time();
}

<commit_msg>posix_spawn() doesn't need a file_actions structure<commit_after>// Copyright 2003-2016 Nicholas J. Kain <njkain at gmail dot com>
// SPDX-License-Identifier: MIT
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <time.h>
#include <sys/time.h>
#include <string.h>
#include <limits.h>
#include <assert.h>
#include <nk/scopeguard.hpp>
extern "C" {
#include "nk/log.h"
#include "nk/pspawn.h"
#include "nk/privs.h"
#include "nk/io.h"
}
#include "ncron.hpp"
#include "sched.hpp"

extern char **environ;

#define COUNT_THRESH 500 /* Arbitrary and untested */

/*
 * returns -1 if below range
 * returns 0 if within range
 * returns 1 if above range
 */
static inline int compare_range(const std::pair<int,int> &r,
                                int v, int wildcard)
{
    if (r.first == wildcard && r.second == wildcard)
        return 0;
    if (r.first == wildcard) {
        if (v < r.second)
            return -1;
        if (v > r.second)
            return 1;
        return 0;
    }
    if (r.second == wildcard) {
        if (v < r.first)
            return -1;
        if (v > r.first)
            return 1;
        return 0;
    }
    if (v < r.first)
        return -1;
    if (v > r.second)
        return 1;
    return 0;
}

/*
 * 1 if range is valid, 0 if not; assumes for constraint and range: (x,y) | x<y
 */
static inline int is_range_valid(const std::pair<int,int> &constraint,
                                 const std::pair<int,int> &range,
                                 int wildcard)
{
    if (range.first < constraint.first && range.first != wildcard)
        return 0;
    if (range.second > constraint.second && range.second != wildcard)
        return 0;
    return 1;
}

/*
 * This fn does the actual constraint.  If our tested value falls within
 * constraint ranges, then nothing is done.  Otherwise, the 'nearest'
 * constraint range that is above the current value is chosen.  If no such
 * range exists, then the next higher unit is incremented by one and the
 * smallest extant valid value is chosen for the constrained unit.
 */
static int compare_list_range_v(const cronentry_t::cst_list &list, int *unit,
                                int *nextunit, const std::pair<int,int> &valid,
                                int wildcard)
{
    int t, smallunit = INT_MAX, dist = INT_MAX, tinyunit = INT_MAX;

    if (list.empty() || !unit || !nextunit)
        return 0;

    /* find the range least distant from our target value */
    for (const auto &i: list) {
        if (i.first < tinyunit)
            tinyunit = i.first;
        if (is_range_valid(valid, i, wildcard)) {
            switch (compare_range(i, *unit, wildcard)) {
                case 1: /* range below our value, avoid if possible */
                    t = *unit - i.second; /* t > 0 */
                    if (t < abs(dist) && dist > 0)
                        dist = t;
                    smallunit = i.first;
                    break;
                default: /* bingo, our value is within a range */
                    return 0;
                case -1: /* range above our value, favor */
                    t = *unit - i.first; /* implicitly, t < 0 */
                    if (abs(t) < abs(dist) || dist > 0)
                        dist = t;
                    smallunit = i.first;
                    break;
            }
        }
    }

    /* All of our constraints are invalid, so act as if all are wildcard. */
    if (tinyunit == INT_MAX)
        return 0;

    if (dist > 0) {
        (*nextunit)++;
        *unit = tinyunit;
    }
    if (dist < 0)
        *unit = smallunit;
    if (dist == 0)
        return 0;
    return 1;
}

/* same as above, but no check on range validity */
static int compare_list_range(const cronentry_t::cst_list &list, int *unit,
                              int *nextunit, int wildcard)
{
    int t, smallunit = INT_MAX, dist = INT_MAX, tinyunit = INT_MAX;

    if (list.empty() || !unit || !nextunit)
        return 0;

    /* find the range least distant from our target value */
    for (const auto &i: list) {
        if (i.first < tinyunit) tinyunit = i.first;
        switch (compare_range(i, *unit, wildcard)) {
            case 1: /* range below our value, avoid if possible */
                t = *unit - i.second; /* t > 0 */
                if (dist > 0 && t < dist)
                    dist = t;
                smallunit = i.first;
                break;
            default: /* bingo, our value is within a range */
                return 0;
            case -1: /* range above our value, favor */
                t = *unit - i.first; /* implicitly, t < 0 */
                if (dist > 0 || t > dist)
                    dist = t;
                smallunit = i.first;
                break;
        }
    }

    if (dist > 0) {
        (*nextunit)++;
        *unit = tinyunit;
    }
    if (dist < 0)
        *unit = smallunit;
    if (dist == 0)
        return 0;
    return 1;
}

static int compare_wday_range(const cronentry_t::cst_list &list, int *unit,
                              int *nextunit)
{
    int dist = INT_MAX, t;
    if (list.empty() || !unit || !nextunit)
        return 0;

    /* find the range least distant from our target value */
    for (const auto &i: list) {
        switch (compare_range(i, *unit, 0)) {
            case 1:
                t = *unit - i.second;
                if (dist > 0 && t < dist)
                    dist = t;
                break;
            case 0:
                return 0;
            case -1:
                t = *unit - i.first;
                if (dist > 0 || t > dist)
                    dist = t;
                break;
        }
    }

    if (dist < 0) {
        nextunit -= dist;
        return 1;
    }

    (*nextunit)++;
    return 1;
}

static std::pair<int,int> valid_day_of_month(int month, int year)
{
    auto ret = std::make_pair<int,int>(1, 31);
    switch (month) {
    case 2: /* we follow the gregorian calendar */
        if (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0))
            ret.second = 29;
        else
            ret.second = 28;
        break;
    case 4: case 6: case 9: case 11: ret.second = 30; default: break;
    }
    return ret;
}

/* entry is obvious, stime is the time we're constraining
 * returns a time value that has been appropriately constrained */
static time_t constrain_time(const cronentry_t &entry, time_t stime)
{
    struct tm *rtime;
    time_t t;
    int count = 0;

    rtime = localtime(&stime);

    for (; count < COUNT_THRESH; count++) {
        t = mktime(rtime);
        rtime = localtime(&t);

        if (compare_list_range(entry.minute, &(rtime->tm_min),
                    &(rtime->tm_hour), 60))
            continue;
        if (compare_list_range(entry.hour, &(rtime->tm_hour),
                    &(rtime->tm_mday), 24))
            continue;
        if (compare_list_range_v(entry.day, &(rtime->tm_mday),
                    &(rtime->tm_mon), valid_day_of_month(rtime->tm_mon,
                        rtime->tm_year), 0))
            continue;
        if (compare_wday_range(entry.weekday, &(rtime->tm_wday),
                    &(rtime->tm_mday)))
            continue;
        if (compare_list_range(entry.month, &(rtime->tm_mon),
                    &(rtime->tm_year), 0))
            continue;
        return mktime(rtime);
    }
    /* Failed to find a suitable time. */
    return 0;
}

/* Used when jobs without exectimes are first loaded. */
void set_initial_exectime(cronentry_t &entry)
{
    struct timespec ts;
    clock_or_die(&ts);
    time_t ttm = constrain_time(entry, ts.tv_sec);
    time_t ttd = ttm - entry.lasttime;
    if (ttd < entry.interval) {
        ttm += entry.interval - ttd;
        ttm = constrain_time(entry, ttm);
    }
    entry.exectime = ttm;
}

/* stupidly advances to next time of execution; performs constraint.  */
void cronentry_t::set_next_time()
{
    struct timespec ts;
    clock_or_die(&ts);
    auto etime = constrain_time(*this, ts.tv_sec + interval);
    exectime = etime > ts.tv_sec ? etime : 0;
}

void save_stack(const std::string &file,
                const std::vector<StackItem> &stack,
                const std::vector<StackItem> &deadstack)
{
    char buf[MAXLINE];

    auto f = fopen(file.c_str(), "w");
    if (!f) {
        log_line("%s: failed to open history file %s for write", __func__, file.c_str());
        std::exit(EXIT_FAILURE);
    }
    SCOPE_EXIT{ fclose(f); };

    auto do_save = [&buf, &f](const std::vector<StackItem> &s) -> bool {
        for (auto &i: s) {
            auto snlen = snprintf(buf, sizeof buf, "%u=%li:%u|%lu\n", i.ce->id,
                                  i.ce->exectime, i.ce->numruns, i.ce->lasttime);
            if (snlen < 0 || static_cast<std::size_t>(snlen) > sizeof buf) {
                log_line("save_stack: Would truncate history entry for job %u; skipping.", i.ce->id);
                continue;
            }
            auto bsize = strlen(buf);
            while (!fwrite(buf, bsize, 1, f)) {
                if (ferror(f))
                    return false;
            }
        }
        return true;
    };
    if (!do_save(stack)) return;
    if (!do_save(deadstack)) return;
}

void cronentry_t::exec(const struct timespec &ts)
{
    pid_t pid;
    if (int ret = nk_pspawn(&pid, command.c_str(), nullptr, nullptr, args.c_str(), environ)) {
        log_line("posix_spawn failed for '%s': %s\n", command.c_str(), strerror(ret));
        return;
    }
    ++numruns;
    lasttime = ts.tv_sec;
    set_next_time();
}

<|endoftext|>


<commit_before><commit_msg>fastuidraw/util/c_array: add method for const-casting a const_c_array<commit_after><|endoftext|>


<commit_before>/*  This file is part of the KDE project
    Copyright (C) 2007 Matthias Kretz <kretz@kde.org>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License version 2 as published by the Free Software Foundation.

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

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to
    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
    Boston, MA 02110-1301, USA.

*/

#include "phonon/config-phonon.h"

#include "kdepluginfactory.h"
#include "kiomediastream.h"

#include <QtCore/QDir>
#include <QtCore/QFile>
#include <QtCore/QtPlugin>
#include <QtCore/QCoreApplication>

#include <kaboutdata.h>
#include <kdebug.h>
#include <kcomponentdata.h>
#include <kglobal.h>
#include <kicon.h>
#include <klibloader.h>
#include <klocale.h>
#include <kmessagebox.h>
#include <kmimetype.h>
#include <knotification.h>
#include <kservice.h>
#include <kservicetypetrader.h>
#include <kconfiggroup.h>
#include <kstandarddirs.h>

namespace Phonon
{

K_GLOBAL_STATIC_WITH_ARGS(KComponentData, mainComponentData, (QCoreApplication::applicationName().toUtf8()))
K_GLOBAL_STATIC_WITH_ARGS(KComponentData, phononComponentData, ("phonon"))

static void ensureMainComponentData()
{
    if (!KGlobal::hasMainComponent()) {
        // a pure Qt application does not have a KComponentData object,
        // we'll give it one.
        *mainComponentData;
        qAddPostRoutine(mainComponentData.destroy);
        Q_ASSERT(KGlobal::hasMainComponent());
    }
}

static const KComponentData &componentData()
{
    ensureMainComponentData();
    return *phononComponentData;
}

KdePlatformPlugin::KdePlatformPlugin()
{
    ensureMainComponentData();
    KGlobal::locale()->insertCatalog(QLatin1String("phonon_kde"));
}

AbstractMediaStream *KdePlatformPlugin::createMediaStream(const QUrl &url, QObject *parent)
{
    return new KioMediaStream(url, parent);
}

QIcon KdePlatformPlugin::icon(const QString &name) const
{
    return KIcon(name);
}

void KdePlatformPlugin::notification(const char *notificationName, const QString &text,
        const QStringList &actions, QObject *receiver,
        const char *actionSlot) const
{
    KNotification *notification = new KNotification(notificationName);
    notification->setComponentData(componentData());
    notification->setText(text);
    //notification->setPixmap(...);
    notification->addContext(QLatin1String("Application"), KGlobal::mainComponent().componentName());
    if (!actions.isEmpty() && receiver && actionSlot) {
        notification->setActions(actions);
        QObject::connect(notification, SIGNAL(activated(unsigned int)), receiver, actionSlot);
    }
    notification->sendEvent();
}

QString KdePlatformPlugin::applicationName() const
{
    ensureMainComponentData();
    const KAboutData *ad = KGlobal::mainComponent().aboutData();
    if (ad) {
        const QString programName = ad->programName();
        if (programName.isEmpty()) {
            return KGlobal::mainComponent().componentName();
        }
        return programName;
    }
    return KGlobal::mainComponent().componentName();
}

#undef PHONON_LOAD_BACKEND_GLOBAL

QObject *KdePlatformPlugin::createBackend(KService::Ptr newService)
{
    QString errorReason;
#ifdef PHONON_LOAD_BACKEND_GLOBAL
    KLibFactory *factory = 0;
    // This code is in here temporarily until NMM gets fixed.
    // Currently the NMM backend will fail with undefined symbols if
    // the backend is not loaded with global symbol resolution
    QObject *backend = 0;
    factory = KLibLoader::self()->factory(newService->library(), QLibrary::ExportExternalSymbolsHint);
    if (!factory) {
        errorReason = KLibLoader::self()->lastErrorMessage();
    } else {
        QObject *backend = factory->create<QObject>();
        if (0 == backend) {
            errorReason = i18n("create method returned 0");
        }
    }
#else
    QObject *backend = newService->createInstance<QObject>(0, QVariantList(), &errorReason);
#endif
    if (0 == backend) {
        const QLatin1String suffix("/phonon_backend/");
        const QStringList libFilter(newService->library() + QLatin1String(".*"));
        foreach (QString libPath, QCoreApplication::libraryPaths()) {
            libPath += suffix;
            const QDir dir(libPath);
            foreach (const QString &pluginName, dir.entryList(libFilter, QDir::Files)) {
                QPluginLoader pluginLoader(libPath + pluginName);
                backend = pluginLoader.instance();
                if (backend) {
                    break;
                }
            }
            if (backend) {
                break;
            }
        }
    }
    if (0 == backend) {
        kError(600) << "Can not create backend object from factory for " <<
            newService->name() << ", " << newService->library() << ":\n" << errorReason;

        KMessageBox::error(0,
                i18n("<qt>Unable to use the %1 Multimedia Backend:
%2</qt>",
                    newService->name(), errorReason));
        return 0;
    }

    kDebug() << "using backend: " << newService->name();
    return backend;
}

QObject *KdePlatformPlugin::createBackend()
{
    // Within this process, display the warning about missing backends
    // only once.
    static bool has_shown = false;
    ensureMainComponentData();
    const KService::List offers = KServiceTypeTrader::self()->query("PhononBackend",
            "Type == 'Service' and [X-KDE-PhononBackendInfo-InterfaceVersion] == 1");
    if (offers.isEmpty()) {
        if (!has_shown) {
#if defined(HAVE_KDE4_MULTIMEDIA)
            KMessageBox::error(0, i18n("Unable to find a Multimedia Backend"));
#endif
            has_shown = true;
        }
        return 0;
    }
    // Flag the warning as not shown, since if the next time the
    // list of backends is suddenly empty again the user should be
    // told.
    has_shown = false;

    KService::List::const_iterator it = offers.begin();
    const KService::List::const_iterator end = offers.end();
    while (it != end) {
        QObject *backend = createBackend(*it);
        if (backend) {
            return backend;
        }
        ++it;
    }
    return 0;
}

QObject *KdePlatformPlugin::createBackend(const QString &library, const QString &version)
{
    ensureMainComponentData();
    QString additionalConstraints = QLatin1String(" and Library == '") + library + QLatin1Char('\'');
    if (!version.isEmpty()) {
        additionalConstraints += QLatin1String(" and [X-KDE-PhononBackendInfo-Version] == '")
            + version + QLatin1Char('\'');
    }
    const KService::List offers = KServiceTypeTrader::self()->query(QLatin1String("PhononBackend"),
            QString("Type == 'Service' and [X-KDE-PhononBackendInfo-InterfaceVersion] == 1%1")
            .arg(additionalConstraints));
    if (offers.isEmpty()) {
        KMessageBox::error(0, i18n("Unable to find the requested Multimedia Backend"));
        return 0;
    }

    KService::List::const_iterator it = offers.begin();
    const KService::List::const_iterator end = offers.end();
    while (it != end) {
        QObject *backend = createBackend(*it);
        if (backend) {
            return backend;
        }
        ++it;
    }
    return 0;
}

bool KdePlatformPlugin::isMimeTypeAvailable(const QString &mimeType) const
{
    ensureMainComponentData();
    const KService::List offers = KServiceTypeTrader::self()->query("PhononBackend",
            "Type == 'Service' and [X-KDE-PhononBackendInfo-InterfaceVersion] == 1");
    if (!offers.isEmpty()) {
        return offers.first()->hasMimeType(KMimeType::mimeType(mimeType).data());
    }
    return false;
}

void KdePlatformPlugin::saveVolume(const QString &outputName, qreal volume)
{
    ensureMainComponentData();
    KConfigGroup config(KGlobal::config(), "Phonon::AudioOutput");
    config.writeEntry(outputName + "_Volume", volume);
}

qreal KdePlatformPlugin::loadVolume(const QString &outputName) const
{
    ensureMainComponentData();
    KConfigGroup config(KGlobal::config(), "Phonon::AudioOutput");
    return config.readEntry<qreal>(outputName + "_Volume", 1.0);
}

} // namespace Phonon

Q_EXPORT_PLUGIN2(phonon_platform_kde, Phonon::KdePlatformPlugin)

#include "kdepluginfactory.moc"
// vim: sw=4 sts=4 et tw=100
<commit_msg>krazy: include own header first; put config.h in angle brackets<commit_after>/*  This file is part of the KDE project
    Copyright (C) 2007 Matthias Kretz <kretz@kde.org>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License version 2 as published by the Free Software Foundation.

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

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to
    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
    Boston, MA 02110-1301, USA.

*/

#include "kdepluginfactory.h"
#include "kiomediastream.h"

#include <phonon/config-phonon.h>

#include <QtCore/QDir>
#include <QtCore/QFile>
#include <QtCore/QtPlugin>
#include <QtCore/QCoreApplication>

#include <kaboutdata.h>
#include <kdebug.h>
#include <kcomponentdata.h>
#include <kglobal.h>
#include <kicon.h>
#include <klibloader.h>
#include <klocale.h>
#include <kmessagebox.h>
#include <kmimetype.h>
#include <knotification.h>
#include <kservice.h>
#include <kservicetypetrader.h>
#include <kconfiggroup.h>
#include <kstandarddirs.h>

namespace Phonon
{

K_GLOBAL_STATIC_WITH_ARGS(KComponentData, mainComponentData, (QCoreApplication::applicationName().toUtf8()))
K_GLOBAL_STATIC_WITH_ARGS(KComponentData, phononComponentData, ("phonon"))

static void ensureMainComponentData()
{
    if (!KGlobal::hasMainComponent()) {
        // a pure Qt application does not have a KComponentData object,
        // we'll give it one.
        *mainComponentData;
        qAddPostRoutine(mainComponentData.destroy);
        Q_ASSERT(KGlobal::hasMainComponent());
    }
}

static const KComponentData &componentData()
{
    ensureMainComponentData();
    return *phononComponentData;
}

KdePlatformPlugin::KdePlatformPlugin()
{
    ensureMainComponentData();
    KGlobal::locale()->insertCatalog(QLatin1String("phonon_kde"));
}

AbstractMediaStream *KdePlatformPlugin::createMediaStream(const QUrl &url, QObject *parent)
{
    return new KioMediaStream(url, parent);
}

QIcon KdePlatformPlugin::icon(const QString &name) const
{
    return KIcon(name);
}

void KdePlatformPlugin::notification(const char *notificationName, const QString &text,
        const QStringList &actions, QObject *receiver,
        const char *actionSlot) const
{
    KNotification *notification = new KNotification(notificationName);
    notification->setComponentData(componentData());
    notification->setText(text);
    //notification->setPixmap(...);
    notification->addContext(QLatin1String("Application"), KGlobal::mainComponent().componentName());
    if (!actions.isEmpty() && receiver && actionSlot) {
        notification->setActions(actions);
        QObject::connect(notification, SIGNAL(activated(unsigned int)), receiver, actionSlot);
    }
    notification->sendEvent();
}

QString KdePlatformPlugin::applicationName() const
{
    ensureMainComponentData();
    const KAboutData *ad = KGlobal::mainComponent().aboutData();
    if (ad) {
        const QString programName = ad->programName();
        if (programName.isEmpty()) {
            return KGlobal::mainComponent().componentName();
        }
        return programName;
    }
    return KGlobal::mainComponent().componentName();
}

#undef PHONON_LOAD_BACKEND_GLOBAL

QObject *KdePlatformPlugin::createBackend(KService::Ptr newService)
{
    QString errorReason;
#ifdef PHONON_LOAD_BACKEND_GLOBAL
    KLibFactory *factory = 0;
    // This code is in here temporarily until NMM gets fixed.
    // Currently the NMM backend will fail with undefined symbols if
    // the backend is not loaded with global symbol resolution
    QObject *backend = 0;
    factory = KLibLoader::self()->factory(newService->library(), QLibrary::ExportExternalSymbolsHint);
    if (!factory) {
        errorReason = KLibLoader::self()->lastErrorMessage();
    } else {
        QObject *backend = factory->create<QObject>();
        if (0 == backend) {
            errorReason = i18n("create method returned 0");
        }
    }
#else
    QObject *backend = newService->createInstance<QObject>(0, QVariantList(), &errorReason);
#endif
    if (0 == backend) {
        const QLatin1String suffix("/phonon_backend/");
        const QStringList libFilter(newService->library() + QLatin1String(".*"));
        foreach (QString libPath, QCoreApplication::libraryPaths()) {
            libPath += suffix;
            const QDir dir(libPath);
            foreach (const QString &pluginName, dir.entryList(libFilter, QDir::Files)) {
                QPluginLoader pluginLoader(libPath + pluginName);
                backend = pluginLoader.instance();
                if (backend) {
                    break;
                }
            }
            if (backend) {
                break;
            }
        }
    }
    if (0 == backend) {
        kError(600) << "Can not create backend object from factory for " <<
            newService->name() << ", " << newService->library() << ":\n" << errorReason;

        KMessageBox::error(0,
                i18n("<qt>Unable to use the %1 Multimedia Backend:
%2</qt>",
                    newService->name(), errorReason));
        return 0;
    }

    kDebug() << "using backend: " << newService->name();
    return backend;
}

QObject *KdePlatformPlugin::createBackend()
{
    // Within this process, display the warning about missing backends
    // only once.
    static bool has_shown = false;
    ensureMainComponentData();
    const KService::List offers = KServiceTypeTrader::self()->query("PhononBackend",
            "Type == 'Service' and [X-KDE-PhononBackendInfo-InterfaceVersion] == 1");
    if (offers.isEmpty()) {
        if (!has_shown) {
#if defined(HAVE_KDE4_MULTIMEDIA)
            KMessageBox::error(0, i18n("Unable to find a Multimedia Backend"));
#endif
            has_shown = true;
        }
        return 0;
    }
    // Flag the warning as not shown, since if the next time the
    // list of backends is suddenly empty again the user should be
    // told.
    has_shown = false;

    KService::List::const_iterator it = offers.begin();
    const KService::List::const_iterator end = offers.end();
    while (it != end) {
        QObject *backend = createBackend(*it);
        if (backend) {
            return backend;
        }
        ++it;
    }
    return 0;
}

QObject *KdePlatformPlugin::createBackend(const QString &library, const QString &version)
{
    ensureMainComponentData();
    QString additionalConstraints = QLatin1String(" and Library == '") + library + QLatin1Char('\'');
    if (!version.isEmpty()) {
        additionalConstraints += QLatin1String(" and [X-KDE-PhononBackendInfo-Version] == '")
            + version + QLatin1Char('\'');
    }
    const KService::List offers = KServiceTypeTrader::self()->query(QLatin1String("PhononBackend"),
            QString("Type == 'Service' and [X-KDE-PhononBackendInfo-InterfaceVersion] == 1%1")
            .arg(additionalConstraints));
    if (offers.isEmpty()) {
        KMessageBox::error(0, i18n("Unable to find the requested Multimedia Backend"));
        return 0;
    }

    KService::List::const_iterator it = offers.begin();
    const KService::List::const_iterator end = offers.end();
    while (it != end) {
        QObject *backend = createBackend(*it);
        if (backend) {
            return backend;
        }
        ++it;
    }
    return 0;
}

bool KdePlatformPlugin::isMimeTypeAvailable(const QString &mimeType) const
{
    ensureMainComponentData();
    const KService::List offers = KServiceTypeTrader::self()->query("PhononBackend",
            "Type == 'Service' and [X-KDE-PhononBackendInfo-InterfaceVersion] == 1");
    if (!offers.isEmpty()) {
        return offers.first()->hasMimeType(KMimeType::mimeType(mimeType).data());
    }
    return false;
}

void KdePlatformPlugin::saveVolume(const QString &outputName, qreal volume)
{
    ensureMainComponentData();
    KConfigGroup config(KGlobal::config(), "Phonon::AudioOutput");
    config.writeEntry(outputName + "_Volume", volume);
}

qreal KdePlatformPlugin::loadVolume(const QString &outputName) const
{
    ensureMainComponentData();
    KConfigGroup config(KGlobal::config(), "Phonon::AudioOutput");
    return config.readEntry<qreal>(outputName + "_Volume", 1.0);
}

} // namespace Phonon

Q_EXPORT_PLUGIN2(phonon_platform_kde, Phonon::KdePlatformPlugin)

#include "kdepluginfactory.moc"
// vim: sw=4 sts=4 et tw=100
<|endoftext|>


<commit_before>#include "NinfoReader.hpp"
#include "utility.hpp"
#include <fstream>

namespace coffeemill
{

void NinfoReader::read()
{
    std::ifstream ninfo(this->filename_);
    if(!ninfo.good())
        throw std::runtime_error("file open error");

    while(!ninfo.eof())
    {
        std::string line;
        std::getline(ninfo, line);
        if(line.empty())        continue;

        line = remove_front<' '>(line);
        if(line.front() == '*') continue;
        if(line.substr(0, 4) == "<<<<") // block start
        {
            this->read_block(ninfo, line);
            continue;
        }

        throw std::invalid_argument("unknown line: " + line);
    }

    return;
}

void NinfoReader::read_block(std::ifstream& ifs, const std::string& line)
{
    NinfoKind kind = this->judge_block(line);

    switch(kind)
    {
      case NinfoKind::Bond:
        {
        data_.emplace(kind, read_block<NinfoBond>(ifs));
        return;
        }
      case NinfoKind::Angl:
        {
        data_.emplace(kind, read_block<NinfoAngl>(ifs));
        return;
        }
      case NinfoKind::Dihd:
        {
        data_.emplace(kind, read_block<NinfoDihd>(ifs));
        return;
        }
      case NinfoKind::Aicg13:
        {
        data_.emplace(kind, read_block<NinfoAicg13>(ifs));
        return;
        }
      case NinfoKind::Aicg14:
        {
        data_.emplace(kind, read_block<NinfoAicg14>(ifs));
        return;
        }
      case NinfoKind::Aicg14p:
        {
        data_.emplace(kind, read_block<NinfoAicg14p>(ifs));
        return;
        }
      case NinfoKind::Contact:
        {
        data_.emplace(kind, read_block<NinfoContact>(ifs));
        return;
        }
      case NinfoKind::BasePair:
        {
        data_.emplace(kind, read_block<NinfoBasePair>(ifs));
        return;
        }
      case NinfoKind::BaseStack:
        {
        data_.emplace(kind, read_block<NinfoBaseStack>(ifs));
        return;
        }
      default:
        throw std::logic_error("invalid ninfo kind");
    }
}

template<typename T_ninfo>
NinfoBlock NinfoReader::read_block(std::ifstream& ifs) const
{
    NinfoBlock block;

    while(!ifs.eof())
    {
        std::string line;
        std::getline(ifs, line);
        if(line.empty())        continue;
        line = remove_front<' '>(line);
        if(line.front() == '*') continue;
        if(line.substr(0,4) == ">>>>") break;

        T_ninfo *elem = new T_ninfo;
        line >> *(elem);
        block.push_back(NinfoSptr(elem));
    }

    return block;
}

NinfoKind NinfoReader::judge_block(const std::string& line) const
{
         if(is_contain(line, NinfoData::bond))      return NinfoKind::Bond;
    else if(is_contain(line, NinfoData::angl))      return NinfoKind::Angl;
    else if(is_contain(line, NinfoData::dihd))      return NinfoKind::Dihd;
    else if(is_contain(line, NinfoData::aicg13))    return NinfoKind::Aicg13;
    else if(is_contain(line, NinfoData::aicg14p))   return NinfoKind::Aicg14p;
    else if(is_contain(line, NinfoData::aicg14))    return NinfoKind::Aicg14;
    else if(is_contain(line, NinfoData::contact))   return NinfoKind::Contact;
    else if(is_contain(line, NinfoData::basepair))  return NinfoKind::BasePair;
    else if(is_contain(line, NinfoData::basestack)) return NinfoKind::BaseStack;
    else throw std::invalid_argument("unknown ninfo block definition: " + line);
}

}
<commit_msg>fix reading loop in NinfoReader<commit_after>#include "NinfoReader.hpp"
#include "utility.hpp"
#include <fstream>

namespace coffeemill
{

void NinfoReader::read()
{
    std::ifstream ninfo(this->filename_);
    if(!ninfo.good())
        throw std::runtime_error("file open error");

    while(!ninfo.eof())
    {
        std::string line;
        std::getline(ninfo, line);
        if(line.empty())        continue;

        line = remove_front<' '>(line);
        if(line.empty()) continue;
        if(line.front() == '*') continue;
        if(line.size() < 4) throw std::invalid_argument("invalid line: " + line);
        if(line.substr(0, 4) == "<<<<") // block start
        {
            this->read_block(ninfo, line);
            continue;
        }

        throw std::invalid_argument("invalid line: " + line);
    }

    return;
}

void NinfoReader::read_block(std::ifstream& ifs, const std::string& line)
{
    NinfoKind kind = this->judge_block(line);

    switch(kind)
    {
      case NinfoKind::Bond:
        {
        data_.emplace(kind, read_block<NinfoBond>(ifs));
        return;
        }
      case NinfoKind::Angl:
        {
        data_.emplace(kind, read_block<NinfoAngl>(ifs));
        return;
        }
      case NinfoKind::Dihd:
        {
        data_.emplace(kind, read_block<NinfoDihd>(ifs));
        return;
        }
      case NinfoKind::Aicg13:
        {
        data_.emplace(kind, read_block<NinfoAicg13>(ifs));
        return;
        }
      case NinfoKind::Aicg14:
        {
        data_.emplace(kind, read_block<NinfoAicg14>(ifs));
        return;
        }
      case NinfoKind::Aicg14p:
        {
        data_.emplace(kind, read_block<NinfoAicg14p>(ifs));
        return;
        }
      case NinfoKind::Contact:
        {
        data_.emplace(kind, read_block<NinfoContact>(ifs));
        return;
        }
      case NinfoKind::BasePair:
        {
        data_.emplace(kind, read_block<NinfoBasePair>(ifs));
        return;
        }
      case NinfoKind::BaseStack:
        {
        data_.emplace(kind, read_block<NinfoBaseStack>(ifs));
        return;
        }
      default:
        throw std::logic_error("invalid ninfo kind");
    }
}

template<typename T_ninfo>
NinfoBlock NinfoReader::read_block(std::ifstream& ifs) const
{
    NinfoBlock block;

    while(!ifs.eof())
    {
        std::string line;
        std::getline(ifs, line);
        if(line.empty())        continue;
        line = remove_front<' '>(line);
        if(line.empty()) continue;
        if(line.front() == '*') continue;
        if(line.size() < 4) throw std::runtime_error("invalid line: " + line);
        if(line.substr(0, 4) == ">>>>") break;

        T_ninfo *elem = new T_ninfo;
        line >> *(elem);
        block.push_back(NinfoSptr(elem));
    }

    return block;
}

NinfoKind NinfoReader::judge_block(const std::string& line) const
{
         if(is_contain(line, NinfoData::bond))      return NinfoKind::Bond;
    else if(is_contain(line, NinfoData::angl))      return NinfoKind::Angl;
    else if(is_contain(line, NinfoData::dihd))      return NinfoKind::Dihd;
    else if(is_contain(line, NinfoData::aicg13))    return NinfoKind::Aicg13;
    else if(is_contain(line, NinfoData::aicg14p))   return NinfoKind::Aicg14p;
    else if(is_contain(line, NinfoData::aicg14))    return NinfoKind::Aicg14;
    else if(is_contain(line, NinfoData::contact))   return NinfoKind::Contact;
    else if(is_contain(line, NinfoData::basepair))  return NinfoKind::BasePair;
    else if(is_contain(line, NinfoData::basestack)) return NinfoKind::BaseStack;
    else throw std::invalid_argument("unknown ninfo block definition: " + line);
}

}
<|endoftext|>


<commit_before>// PythonStuff.cpp

#include "stdafx.h"
#include <wx/file.h>
#include <wx/mimetype.h>
#include "PythonStuff.h"
#include "ProgramCanvas.h"
#include "OutputCanvas.h"
#include "Program.h"

static bool write_python_file(const wxString& python_file_path)
{
	wxFile ofs(python_file_path.c_str(), wxFile::write);
	if(!ofs.IsOpened())return false;

	ofs.Write(theApp.m_program_canvas->m_textCtrl->GetValue());

	return true;
}

bool HeeksPyPostProcess()
{
	try{
		theApp.m_output_canvas->m_textCtrl->Clear(); // clear the output window

		// write the python file
		wxString file_str = theApp.GetDllFolder() + wxString(_T("/post.py"));
		if(!write_python_file(file_str))
		{
			wxMessageBox(_T("couldn't write post.py!"));
		}
		else
		{
			::wxSetWorkingDirectory(theApp.GetDllFolder());

			// call the python file
#ifdef WIN32
			wxString batch_file_str = theApp.GetDllFolder() + wxString(_T("/post.bat"));
			wxExecute(batch_file_str, wxEXEC_SYNC);
#else
			wxString py_file_str = theApp.GetDllFolder() + wxString(_T("/post.py"));
			wxExecute(wxString(_T("python ")) + py_file_str, wxEXEC_SYNC);
#endif
			// in Windows, at least, executing the bat file was making HeeksCAD change it's Z order
			heeksCAD->GetMainFrame()->Raise();
			return true;
		}
	}
	catch(...)
	{
		wxMessageBox(_T("Error while post-processing the program!"));
	}
	return false;
}

bool HeeksPyBackplot(const wxString &filepath)
{
	try{
		theApp.m_output_canvas->m_textCtrl->Clear(); // clear the output window

		::wxSetWorkingDirectory(theApp.GetDllFolder());

		// call the python file
#ifdef WIN32
		wxString py_file_str = wxString(_T("\"")) + theApp.GetDllFolder() + _T("/nc_read.bat\" iso ") + filepath;
		wxExecute(py_file_str, wxEXEC_SYNC);
#else
		wxString py_file_str = wxString(_T("\"")) + theApp.GetDllFolder() + wxString(_T("/nc/iso_read.py\" ")) + filepath;
		wxExecute(wxString(_T("python ")) + py_file_str, wxEXEC_SYNC);
#endif
		// in Windows, at least, executing the bat file was making HeeksCAD change it's Z order
		heeksCAD->GetMainFrame()->Raise();

		// there should now be nccode.xml written
#ifdef WIN32
		wxString xml_file_str = theApp.GetDllFolder() + wxString(_T("/")) + filepath + wxString(_T(".xml"));
#else
		wxString xml_file_str = theApp.GetDllFolder() + wxString(_T("/")) + filepath + wxString(_T(".nc.xml"));
#endif
		wxFile ofs(xml_file_str.c_str());
		if(!ofs.IsOpened())
		{
			wxMessageBox(wxString(_("Couldn't open file")) + _T(" - ") + xml_file_str);
			return false;
		}

		// read the xml file, just like paste, into the program
		heeksCAD->OpenXMLFile(xml_file_str, true, theApp.m_program);
		heeksCAD->Repaint();

		return true;
	}
	catch(...)
	{
		wxMessageBox(_T("Error while backplotting the program!"));
	}
	return false;
}

<commit_msg>changed backplot file from .xml to .nc.xml<commit_after>// PythonStuff.cpp

#include "stdafx.h"
#include <wx/file.h>
#include <wx/mimetype.h>
#include "PythonStuff.h"
#include "ProgramCanvas.h"
#include "OutputCanvas.h"
#include "Program.h"

static bool write_python_file(const wxString& python_file_path)
{
	wxFile ofs(python_file_path.c_str(), wxFile::write);
	if(!ofs.IsOpened())return false;

	ofs.Write(theApp.m_program_canvas->m_textCtrl->GetValue());

	return true;
}

bool HeeksPyPostProcess()
{
	try{
		theApp.m_output_canvas->m_textCtrl->Clear(); // clear the output window

		// write the python file
		wxString file_str = theApp.GetDllFolder() + wxString(_T("/post.py"));
		if(!write_python_file(file_str))
		{
			wxMessageBox(_T("couldn't write post.py!"));
		}
		else
		{
			::wxSetWorkingDirectory(theApp.GetDllFolder());

			// call the python file
#ifdef WIN32
			wxString batch_file_str = theApp.GetDllFolder() + wxString(_T("/post.bat"));
			wxExecute(batch_file_str, wxEXEC_SYNC);
#else
			wxString py_file_str = theApp.GetDllFolder() + wxString(_T("/post.py"));
			wxExecute(wxString(_T("python ")) + py_file_str, wxEXEC_SYNC);
#endif
			// in Windows, at least, executing the bat file was making HeeksCAD change it's Z order
			heeksCAD->GetMainFrame()->Raise();
			return true;
		}
	}
	catch(...)
	{
		wxMessageBox(_T("Error while post-processing the program!"));
	}
	return false;
}

bool HeeksPyBackplot(const wxString &filepath)
{
	try{
		theApp.m_output_canvas->m_textCtrl->Clear(); // clear the output window

		::wxSetWorkingDirectory(theApp.GetDllFolder());

		// call the python file
#ifdef WIN32
		wxString py_file_str = wxString(_T("\"")) + theApp.GetDllFolder() + _T("/nc_read.bat\" iso ") + filepath;
		wxExecute(py_file_str, wxEXEC_SYNC);
#else
		wxString py_file_str = wxString(_T("\"")) + theApp.GetDllFolder() + wxString(_T("/nc/iso_read.py\" ")) + filepath;
		wxExecute(wxString(_T("python ")) + py_file_str, wxEXEC_SYNC);
#endif
		// in Windows, at least, executing the bat file was making HeeksCAD change it's Z order
		heeksCAD->GetMainFrame()->Raise();

		// there should now be a .nc.xml written
		wxString xml_file_str = theApp.GetDllFolder() + wxString(_T("/")) + filepath + wxString(_T(".nc.xml"));
		wxFile ofs(xml_file_str.c_str());
		if(!ofs.IsOpened())
		{
			wxMessageBox(wxString(_("Couldn't open file")) + _T(" - ") + xml_file_str);
			return false;
		}

		// read the xml file, just like paste, into the program
		heeksCAD->OpenXMLFile(xml_file_str, true, theApp.m_program);
		heeksCAD->Repaint();

		return true;
	}
	catch(...)
	{
		wxMessageBox(_T("Error while backplotting the program!"));
	}
	return false;
}

<|endoftext|>