<commit_before>// csabbg_midreturn.cpp                                               -*-C++-*-

#include <clang/AST/ASTContext.h>
#include <clang/AST/Decl.h>
#include <clang/AST/DeclBase.h>
#include <clang/AST/Stmt.h>
#include <clang/AST/StmtIterator.h>
#include <clang/ASTMatchers/ASTMatchFinder.h>
#include <clang/ASTMatchers/ASTMatchers.h>
#include <clang/ASTMatchers/ASTMatchersInternal.h>
#include <clang/Basic/Diagnostic.h>
#include <clang/Basic/SourceLocation.h>
#include <clang/Basic/SourceManager.h>
#include <clang/Rewrite/Core/Rewriter.h>
#include <csabase_analyser.h>
#include <csabase_diagnostic_builder.h>
#include <csabase_location.h>
#include <csabase_ppobserver.h>
#include <csabase_registercheck.h>
#include <csabase_util.h>
#include <llvm/ADT/Optional.h>
#include <llvm/ADT/StringRef.h>
#include <llvm/ADT/VariadicFunction.h>
#include <llvm/Support/Casting.h>
#include <stddef.h>
#include <utils/event.hpp>
#include <utils/function.hpp>
#include <set>
#include <sstream>
#include <string>

namespace csabase { class Visitor; }

using namespace clang;
using namespace clang::ast_matchers;
using namespace csabase;

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

static std::string const check_name("mid-return");

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

namespace
{

// Data attached to analyzer for this check.
struct data
{
    std::set<const Stmt*>    d_last_returns;  // Last top-level 'return'
    std::set<const Stmt*>    d_all_returns;   // All 'return'
    std::set<SourceLocation> d_rcs;           // Suppression comments
};

// Callback object for inspecting comments.
struct comments
{
    Analyser& d_analyser;

    comments(Analyser& analyser) : d_analyser(analyser) {}

    void operator()(SourceRange range)
    {
        Location location(d_analyser.get_location(range.getBegin()));
        if (d_analyser.is_component(location.file())) {
            std::string comment(d_analyser.get_source(range));
            size_t rpos = comment.rfind("// RETURN");
            if (rpos != comment.npos) {
                d_analyser.attachment<data>().d_rcs.insert(
                    range.getBegin().getLocWithOffset(rpos));
            }
        }
    }
};

const internal::DynTypedMatcher &
return_matcher()
    // Return an AST matcher which looks for return statements.
{
    static const internal::DynTypedMatcher matcher =
        decl(forEachDescendant(returnStmt().bind("return")));
    return matcher;
}

// Callback object invoked upon completion.
struct report
{
    Analyser& d_analyser;
    data& d_data;

    report(Analyser& analyser)
        : d_analyser(analyser)
        , d_data(analyser.attachment<data>()) { }

    // Function for searching for final return statements.
    const ReturnStmt* last_return(ConstStmtRange s)
    {
        const ReturnStmt* ret = 0;
        for (; s; ++s) {
            if (llvm::dyn_cast<CompoundStmt>(*s)) {
                // Recurse into simple compound statements.
                ret = last_return((*s)->children());
            } else {
                // Try to cast each statement to a ReturnStmt. Therefore 'ret'
                // will only be non-zero if the final statement is a 'return'.
                ret = llvm::dyn_cast<ReturnStmt>(*s);
            }
        }
        return ret;
    }

    void match_return(const BoundNodes& nodes)
    {
        const ReturnStmt *ret = nodes.getNodeAs<ReturnStmt>("return");

        if (!d_analyser.is_component(ret->getLocStart())) {
            return;                                                   // RETURN
        }

        // If the statement is contained in a function template specialization
        // (even nested within local classes) ignore it - the original in the
        // template will be processed.
        const FunctionDecl* func = d_analyser.get_parent<FunctionDecl>(ret);
        d_data.d_last_returns.insert(last_return(func->getBody()->children()));
        do {
            if (func->isTemplateInstantiation()) {
                return;                                               // RETURN
            }
        } while (0 != (func = d_analyser.get_parent<FunctionDecl>(func)));
        d_data.d_all_returns.insert(ret);
    }

    void operator()()
    {
        MatchFinder mf;
        OnMatch<report, &report::match_return> m1(this);
        mf.addDynamicMatcher(return_matcher(), &m1);
        mf.match(*d_analyser.context()->getTranslationUnitDecl(),
                 *d_analyser.context());

        process_all_returns(
            d_data.d_all_returns.begin(), d_data.d_all_returns.end());
    }

    void process_all_returns(std::set<const Stmt*>::iterator begin,
                             std::set<const Stmt*>::iterator end)
    {
        const data& d = d_analyser.attachment<data>();
        for (std::set<const Stmt*>::iterator it = begin; it != end; ++it) {
            // Ignore final top-level return statements.
            if (!d.d_last_returns.count(*it) &&
                d_analyser.is_component(*it) &&
                !is_commented(*it, d.d_rcs.begin(), d.d_rcs.end())) {
                d_analyser.report(*it, check_name, "MR01",
                        "Mid-function 'return' requires '// RETURN' comment");
                SourceRange line_range =
                    d_analyser.get_line_range((*it)->getLocEnd());
                if (line_range.isValid()) {
                    llvm::StringRef line = d_analyser.get_source(line_range);
                    std::string tag = (line.size() < 70
                                       ? std::string(70 - line.size(), ' ')
                                       : "\n" + std::string(70, ' ')
                                      ) + "// RETURN";
                    d_analyser.report(*it, check_name, "MR01",
                                      "Correct text is\n%0",
                                      false, DiagnosticsEngine::Note)
                        << line.str() + tag;
                    d_analyser.rewriter().InsertTextAfter(
                        line_range.getEnd(), tag);
                }
            }
        }
    }

    // Determine if a statement has a proper '// RETURN' comment.
    bool is_commented(const Stmt* stmt,
                      std::set<SourceLocation>::iterator comments_begin,
                      std::set<SourceLocation>::iterator comments_end)
    {
        if (!d_analyser.is_component(stmt)) {
            return true;                                              // RETURN
        }

        SourceManager& m = d_analyser.manager();
        SourceLocation loc = m.getFileLoc(stmt->getLocEnd());
        unsigned       sline = m.getPresumedLineNumber(loc);
        unsigned       scolm = m.getPresumedColumnNumber(loc);
        FileID  sfile = m.getFileID(loc);

        for (std::set<SourceLocation>::iterator it = comments_begin;
             it != comments_end;
             ++it) {
            unsigned      cline = m.getPresumedLineNumber(*it);
            unsigned      ccolm = m.getPresumedColumnNumber(*it);
            FileID cfile = m.getFileID(*it);

            if (   (cline == sline || (scolm >= 69 && cline == sline + 1))
                && cfile == sfile) {
                if (ccolm != 71) {
                    std::ostringstream ss;
                    ss << "'// RETURN' comment must end in column 79, "
                       << "not " << (ccolm + 8);
                    if (scolm >= 69 && ccolm > 71) {
                        ss << " (place it alone on the next line)";
                    }
                    d_analyser.report(*it, check_name, "MR02", ss.str());
                    SourceRange line_range = d_analyser.get_line_range(*it);
                    llvm::StringRef line = d_analyser.get_source(line_range)
                                               .slice(0, ccolm - 1)
                                               .rtrim();
                    std::string tag = (line.size() < 70
                                       ? std::string(70 - line.size(), ' ')
                                       : "\n" + std::string(70, ' ')
                                      ) + "// RETURN";
                    d_analyser.report(*it, check_name, "MR01",
                            "Correct text is\n%0",
                            false, DiagnosticsEngine::Note)
                        << line.str() + tag;
                    line_range.setBegin(
                        line_range.getBegin().getLocWithOffset(line.size()));
                    d_analyser.rewriter().ReplaceText(line_range, tag);
                }
                return true;
            }
        }
        return false;
    }
};

void subscribe(Analyser& analyser, Visitor&, PPObserver& observer)
{
    analyser.onTranslationUnitDone += report(analyser);
    observer.onComment += comments(analyser);
}

}  // close anonymous namespace

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

static RegisterCheck c3(check_name, &subscribe);

// ----------------------------------------------------------------------------
// Copyright (C) 2014 Bloomberg Finance L.P.
//
// 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.
// ----------------------------- END-OF-FILE ----------------------------------
<commit_msg>Work around that 'a.def'->getEndLoc() points to 'd'.<commit_after>// csabbg_midreturn.cpp                                               -*-C++-*-

#include <clang/AST/ASTContext.h>
#include <clang/AST/Decl.h>
#include <clang/AST/DeclBase.h>
#include <clang/AST/Stmt.h>
#include <clang/AST/StmtIterator.h>
#include <clang/ASTMatchers/ASTMatchFinder.h>
#include <clang/ASTMatchers/ASTMatchers.h>
#include <clang/ASTMatchers/ASTMatchersInternal.h>
#include <clang/Basic/Diagnostic.h>
#include <clang/Basic/SourceLocation.h>
#include <clang/Basic/SourceManager.h>
#include <clang/Lex/Lexer.h>
#include <clang/Rewrite/Core/Rewriter.h>
#include <csabase_analyser.h>
#include <csabase_diagnostic_builder.h>
#include <csabase_location.h>
#include <csabase_ppobserver.h>
#include <csabase_registercheck.h>
#include <csabase_util.h>
#include <llvm/ADT/Optional.h>
#include <llvm/ADT/StringRef.h>
#include <llvm/ADT/VariadicFunction.h>
#include <llvm/Support/Casting.h>
#include <stddef.h>
#include <utils/event.hpp>
#include <utils/function.hpp>
#include <set>
#include <sstream>
#include <string>

namespace csabase { class Visitor; }

using namespace clang;
using namespace clang::ast_matchers;
using namespace csabase;

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

static std::string const check_name("mid-return");

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

namespace
{

// Data attached to analyzer for this check.
struct data
{
    std::set<const ReturnStmt*> d_last_returns;  // Last top-level 'return'
    std::set<const ReturnStmt*> d_all_returns;   // All 'return'
    std::set<SourceLocation>    d_rcs;           // Suppression comments
};

// Callback object for inspecting comments.
struct comments
{
    Analyser& d_analyser;

    comments(Analyser& analyser) : d_analyser(analyser) {}

    void operator()(SourceRange range)
    {
        Location location(d_analyser.get_location(range.getBegin()));
        if (d_analyser.is_component(location.file())) {
            std::string comment(d_analyser.get_source(range));
            size_t rpos = comment.rfind("// RETURN");
            if (rpos != comment.npos) {
                d_analyser.attachment<data>().d_rcs.insert(
                    range.getBegin().getLocWithOffset(rpos));
            }
        }
    }
};

const internal::DynTypedMatcher &
return_matcher()
    // Return an AST matcher which looks for return statements.
{
    static const internal::DynTypedMatcher matcher =
        decl(forEachDescendant(returnStmt().bind("return")));
    return matcher;
}

// Callback object invoked upon completion.
struct report
{
    Analyser& d_analyser;
    data& d_data;

    report(Analyser& analyser)
        : d_analyser(analyser)
        , d_data(analyser.attachment<data>()) { }

    // Function for searching for final return statements.
    const ReturnStmt* last_return(ConstStmtRange s)
    {
        const ReturnStmt* ret = 0;
        for (; s; ++s) {
            if (llvm::dyn_cast<CompoundStmt>(*s)) {
                // Recurse into simple compound statements.
                ret = last_return((*s)->children());
            } else {
                // Try to cast each statement to a ReturnStmt. Therefore 'ret'
                // will only be non-zero if the final statement is a 'return'.
                ret = llvm::dyn_cast<ReturnStmt>(*s);
            }
        }
        return ret;
    }

    void match_return(const BoundNodes& nodes)
    {
        const ReturnStmt *ret = nodes.getNodeAs<ReturnStmt>("return");

        if (!d_analyser.is_component(ret->getLocStart())) {
            return;                                                   // RETURN
        }

        // If the statement is contained in a function template specialization
        // (even nested within local classes) ignore it - the original in the
        // template will be processed.
        const FunctionDecl* func = d_analyser.get_parent<FunctionDecl>(ret);
        d_data.d_last_returns.insert(last_return(func->getBody()->children()));
        do {
            if (func->isTemplateInstantiation()) {
                return;                                               // RETURN
            }
        } while (0 != (func = d_analyser.get_parent<FunctionDecl>(func)));
        d_data.d_all_returns.insert(ret);
    }

    void operator()()
    {
        MatchFinder mf;
        OnMatch<report, &report::match_return> m1(this);
        mf.addDynamicMatcher(return_matcher(), &m1);
        mf.match(*d_analyser.context()->getTranslationUnitDecl(),
                 *d_analyser.context());

        process_all_returns(
            d_data.d_all_returns.begin(), d_data.d_all_returns.end());
    }

    void process_all_returns(std::set<const ReturnStmt*>::iterator begin,
                             std::set<const ReturnStmt*>::iterator end)
    {
        const data& d = d_analyser.attachment<data>();
        for (std::set<const ReturnStmt*>::iterator it = begin;
             it != end;
             ++it) {
            // Ignore final top-level return statements.
            if (!d.d_last_returns.count(*it) &&
                d_analyser.is_component(*it) &&
                !is_commented(*it, d.d_rcs.begin(), d.d_rcs.end())) {
                d_analyser.report(*it, check_name, "MR01",
                        "Mid-function 'return' requires '// RETURN' comment");
                SourceRange line_range =
                    d_analyser.get_line_range((*it)->getLocEnd());
                if (line_range.isValid()) {
                    llvm::StringRef line = d_analyser.get_source(line_range);
                    std::string tag = (line.size() < 70
                                       ? std::string(70 - line.size(), ' ')
                                       : "\n" + std::string(70, ' ')
                                      ) + "// RETURN";
                    d_analyser.report(*it, check_name, "MR01",
                                      "Correct text is\n%0",
                                      false, DiagnosticsEngine::Note)
                        << line.str() + tag;
                    d_analyser.rewriter().InsertTextAfter(
                        line_range.getEnd(), tag);
                }
            }
        }
    }

    // Determine if a statement has a proper '// RETURN' comment.
    bool is_commented(const ReturnStmt* stmt,
                      std::set<SourceLocation>::iterator comments_begin,
                      std::set<SourceLocation>::iterator comments_end)
    {
        if (!d_analyser.is_component(stmt)) {
            return true;                                              // RETURN
        }

        SourceManager& m = d_analyser.manager();
        // This "getLocForEndOfToken" weirdness is necessary because for a
        // member expression (like "a.def"), "stmt->getLocEnd()" returns the
        // beginning of the member instead of the end (i.e., 'd', not 'f')!
        SourceLocation loc = m.getFileLoc(Lexer::getLocForEndOfToken(
            stmt->getLocEnd(), 0, m, d_analyser.context()->getLangOpts()));
        unsigned       sline = m.getPresumedLineNumber(loc);
        unsigned       scolm = m.getPresumedColumnNumber(loc);
        FileID         sfile = m.getFileID(loc);

        for (std::set<SourceLocation>::iterator it = comments_begin;
             it != comments_end;
             ++it) {
            unsigned      cline = m.getPresumedLineNumber(*it);
            unsigned      ccolm = m.getPresumedColumnNumber(*it);
            FileID cfile = m.getFileID(*it);

            if (   (cline == sline || (scolm >= 69 && cline == sline + 1))
                && cfile == sfile) {
                if (ccolm != 71) {
                    std::ostringstream ss;
                    ss << "'// RETURN' comment must end in column 79, "
                       << "not " << (ccolm + 8);
                    if (scolm >= 69 && ccolm > 71) {
                        ss << " (place it alone on the next line)";
                    }
                    d_analyser.report(*it, check_name, "MR02", ss.str());
                    SourceRange line_range = d_analyser.get_line_range(*it);
                    llvm::StringRef line = d_analyser.get_source(line_range)
                                               .slice(0, ccolm - 1)
                                               .rtrim();
                    std::string tag = (line.size() < 70
                                       ? std::string(70 - line.size(), ' ')
                                       : "\n" + std::string(70, ' ')
                                      ) + "// RETURN";
                    d_analyser.report(*it, check_name, "MR01",
                            "Correct text is\n%0",
                            false, DiagnosticsEngine::Note)
                        << line.str() + tag;
                    line_range.setBegin(
                        line_range.getBegin().getLocWithOffset(line.size()));
                    d_analyser.rewriter().ReplaceText(line_range, tag);
                }
                return true;
            }
        }
        return false;
    }
};

void subscribe(Analyser& analyser, Visitor&, PPObserver& observer)
{
    analyser.onTranslationUnitDone += report(analyser);
    observer.onComment += comments(analyser);
}

}  // close anonymous namespace

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

static RegisterCheck c3(check_name, &subscribe);

// ----------------------------------------------------------------------------
// Copyright (C) 2014 Bloomberg Finance L.P.
//
// 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.
// ----------------------------- END-OF-FILE ----------------------------------
<|endoftext|>


<commit_before>#include "command.hh"
#include "common-args.hh"
#include "shared.hh"
#include "progress-bar.hh"
#include "eval.hh"
#include <nlohmann/json.hpp>
#include <queue>

using namespace nix;

struct CmdFlakeList : StoreCommand, MixEvalArgs
{
    std::string name() override
    {
        return "list";
    }

    std::string description() override
    {
        return "list available Nix flakes";
    }

    void run(nix::ref<nix::Store> store) override
    {
        auto evalState = std::make_shared<EvalState>(searchPath, store);

        auto registries = evalState->getFlakeRegistries();

        stopProgressBar();

        for (auto & registry : registries)
            for (auto & entry : registry->entries)
                std::cout << entry.first.to_string() << " " << entry.second.to_string() << "\n";
    }
};

void printFlakeInfo(Flake & flake, bool json) {
    if (json) {
        nlohmann::json j;
        j["id"] = flake.id;
        j["location"] = flake.path;
        j["description"] = flake.description;
        std::cout << j.dump(4) << std::endl;
    } else {
        std::cout << "ID:          " << flake.id << "\n";
        std::cout << "Description: " << flake.description << "\n";
        std::cout << "Location:    " << flake.path << "\n";
    }
}

void printNonFlakeInfo(NonFlake & nonFlake, bool json) {
    if (json) {
        nlohmann::json j;
        j["name"] = nonFlake.alias;
        j["location"] = nonFlake.path;
        std::cout << j.dump(4) << std::endl;
    } else {
        std::cout << "name:        " << nonFlake.alias << "\n";
        std::cout << "Location:    " << nonFlake.path << "\n";
    }
}

struct CmdFlakeDeps : FlakeCommand, MixJSON, StoreCommand, MixEvalArgs
{
    std::string name() override
    {
        return "deps";
    }

    std::string description() override
    {
        return "list informaton about dependencies";
    }

    void run(nix::ref<nix::Store> store) override
    {
        auto evalState = std::make_shared<EvalState>(searchPath, store);

        FlakeRef flakeRef(flakeUri);

        Dependencies deps = resolveFlake(*evalState, flakeRef, true);

        std::queue<Dependencies> todo;
        todo.push(deps);

        while (!todo.empty()) {
            deps = todo.front();
            todo.pop();

            for (auto & nonFlake : deps.nonFlakeDeps)
                printNonFlakeInfo(nonFlake, json);

            for (auto & newDeps : deps.flakeDeps)
                todo.push(newDeps);
        }
    }
};

struct CmdFlakeUpdate : StoreCommand, GitRepoCommand, MixEvalArgs
{
    std::string name() override
    {
        return "update";
    }

    std::string description() override
    {
        return "update flake lock file";
    }

    void run(nix::ref<nix::Store> store) override
    {
        auto evalState = std::make_shared<EvalState>(searchPath, store);

        if (gitPath == "") gitPath = absPath(".");
        updateLockFile(*evalState, gitPath);
    }
};

struct CmdFlakeInfo : FlakeCommand, MixJSON, MixEvalArgs, StoreCommand
{
    std::string name() override
    {
        return "info";
    }

    std::string description() override
    {
        return "list info about a given flake";
    }

    CmdFlakeInfo () { evalSettings.pureEval = false; }

    void run(nix::ref<nix::Store> store) override
    {
        auto evalState = std::make_shared<EvalState>(searchPath, store);
        nix::Flake flake = nix::getFlake(*evalState, FlakeRef(flakeUri), true);
        printFlakeInfo(flake, json);
    }
};

struct CmdFlakeAdd : MixEvalArgs, Command
{
    FlakeUri alias;
    FlakeUri uri;

    std::string name() override
    {
        return "add";
    }

    std::string description() override
    {
        return "upsert flake in user flake registry";
    }

    CmdFlakeAdd()
    {
        expectArg("alias", &alias);
        expectArg("flake-uri", &uri);
        evalSettings.pureEval = false;
    }

    void run() override
    {
        FlakeRef aliasRef(alias);
        Path userRegistryPath = getUserRegistryPath();
        auto userRegistry = readRegistry(userRegistryPath);
        userRegistry->entries.erase(aliasRef);
        userRegistry->entries.insert_or_assign(aliasRef, FlakeRef(uri));
        writeRegistry(*userRegistry, userRegistryPath);
    }
};

struct CmdFlakeRemove : virtual Args, MixEvalArgs, Command
{
    FlakeUri alias;

    std::string name() override
    {
        return "remove";
    }

    std::string description() override
    {
        return "remove flake from user flake registry";
    }

    CmdFlakeRemove()
    {
        expectArg("alias", &alias);
        evalSettings.pureEval = false;
    }

    void run() override
    {
        Path userRegistryPath = getUserRegistryPath();
        auto userRegistry = readRegistry(userRegistryPath);
        userRegistry->entries.erase(FlakeRef(alias));
        writeRegistry(*userRegistry, userRegistryPath);
    }
};

struct CmdFlakePin : virtual Args, StoreCommand, MixEvalArgs
{
    FlakeUri alias;

    std::string name() override
    {
        return "pin";
    }

    std::string description() override
    {
        return "pin flake require in user flake registry";
    }

    CmdFlakePin()
    {
        expectArg("alias", &alias);
        evalSettings.pureEval = false;
    }

    void run(nix::ref<nix::Store> store) override
    {
        auto evalState = std::make_shared<EvalState>(searchPath, store);

        Path userRegistryPath = getUserRegistryPath();
        FlakeRegistry userRegistry = *readRegistry(userRegistryPath);
        auto it = userRegistry.entries.find(FlakeRef(alias));
        if (it != userRegistry.entries.end()) {
            it->second = getFlake(*evalState, it->second, true).ref;
            // The 'ref' in 'flake' is immutable.
            writeRegistry(userRegistry, userRegistryPath);
        } else {
            std::shared_ptr<FlakeRegistry> globalReg = getGlobalRegistry();
            it = globalReg->entries.find(FlakeRef(alias));
            if (it != globalReg->entries.end()) {
                FlakeRef newRef = getFlake(*evalState, it->second, true).ref;
                userRegistry.entries.insert_or_assign(alias, newRef);
                writeRegistry(userRegistry, userRegistryPath);
            } else
                throw Error("the flake alias '%s' does not exist in the user or global registry", alias);
        }
    }
};

struct CmdFlakeInit : virtual Args, Command
{
    std::string name() override
    {
        return "init";
    }

    std::string description() override
    {
        return "create a skeleton 'flake.nix' file in the current directory";
    }

    void run() override
    {
        Path flakeDir = absPath(".");

        if (!pathExists(flakeDir + "/.git"))
            throw Error("the directory '%s' is not a Git repository", flakeDir);

        Path flakePath = flakeDir + "/flake.nix";

        if (pathExists(flakePath))
            throw Error("file '%s' already exists", flakePath);

        writeFile(flakePath,
#include "flake-template.nix.gen.hh"
            );
    }
};

struct CmdFlake : virtual MultiCommand, virtual Command
{
    CmdFlake()
        : MultiCommand({make_ref<CmdFlakeList>()
            , make_ref<CmdFlakeUpdate>()
            , make_ref<CmdFlakeInfo>()
            , make_ref<CmdFlakeDeps>()
            , make_ref<CmdFlakeAdd>()
            , make_ref<CmdFlakeRemove>()
            , make_ref<CmdFlakePin>()
            , make_ref<CmdFlakeInit>()
          })
    {
    }

    std::string name() override
    {
        return "flake";
    }

    std::string description() override
    {
        return "manage Nix flakes";
    }

    void run() override
    {
        if (!command)
            throw UsageError("'nix flake' requires a sub-command.");
        command->run();
    }

    void printHelp(const string & programName, std::ostream & out) override
    {
        MultiCommand::printHelp(programName, out);
    }
};

static RegisterCommand r1(make_ref<CmdFlake>());
<commit_msg>Add FIXME for pureEval<commit_after>#include "command.hh"
#include "common-args.hh"
#include "shared.hh"
#include "progress-bar.hh"
#include "eval.hh"
#include <nlohmann/json.hpp>
#include <queue>

using namespace nix;

struct CmdFlakeList : StoreCommand, MixEvalArgs
{
    std::string name() override
    {
        return "list";
    }

    std::string description() override
    {
        return "list available Nix flakes";
    }

    void run(nix::ref<nix::Store> store) override
    {
        auto evalState = std::make_shared<EvalState>(searchPath, store);

        auto registries = evalState->getFlakeRegistries();

        stopProgressBar();

        for (auto & registry : registries)
            for (auto & entry : registry->entries)
                std::cout << entry.first.to_string() << " " << entry.second.to_string() << "\n";
    }
};

void printFlakeInfo(Flake & flake, bool json) {
    if (json) {
        nlohmann::json j;
        j["id"] = flake.id;
        j["location"] = flake.path;
        j["description"] = flake.description;
        std::cout << j.dump(4) << std::endl;
    } else {
        std::cout << "ID:          " << flake.id << "\n";
        std::cout << "Description: " << flake.description << "\n";
        std::cout << "Location:    " << flake.path << "\n";
    }
}

void printNonFlakeInfo(NonFlake & nonFlake, bool json) {
    if (json) {
        nlohmann::json j;
        j["name"] = nonFlake.alias;
        j["location"] = nonFlake.path;
        std::cout << j.dump(4) << std::endl;
    } else {
        std::cout << "name:        " << nonFlake.alias << "\n";
        std::cout << "Location:    " << nonFlake.path << "\n";
    }
}

struct CmdFlakeDeps : FlakeCommand, MixJSON, StoreCommand, MixEvalArgs
{
    std::string name() override
    {
        return "deps";
    }

    std::string description() override
    {
        return "list informaton about dependencies";
    }

    void run(nix::ref<nix::Store> store) override
    {
        auto evalState = std::make_shared<EvalState>(searchPath, store);

        FlakeRef flakeRef(flakeUri);

        Dependencies deps = resolveFlake(*evalState, flakeRef, true);

        std::queue<Dependencies> todo;
        todo.push(deps);

        while (!todo.empty()) {
            deps = todo.front();
            todo.pop();

            for (auto & nonFlake : deps.nonFlakeDeps)
                printNonFlakeInfo(nonFlake, json);

            for (auto & newDeps : deps.flakeDeps)
                todo.push(newDeps);
        }
    }
};

struct CmdFlakeUpdate : StoreCommand, GitRepoCommand, MixEvalArgs
{
    std::string name() override
    {
        return "update";
    }

    std::string description() override
    {
        return "update flake lock file";
    }

    void run(nix::ref<nix::Store> store) override
    {
        auto evalState = std::make_shared<EvalState>(searchPath, store);

        if (gitPath == "") gitPath = absPath(".");
        updateLockFile(*evalState, gitPath);
    }
};

struct CmdFlakeInfo : FlakeCommand, MixJSON, MixEvalArgs, StoreCommand
{
    std::string name() override
    {
        return "info";
    }

    std::string description() override
    {
        return "list info about a given flake";
    }

    CmdFlakeInfo () { evalSettings.pureEval = false; /* FIXME */ }

    void run(nix::ref<nix::Store> store) override
    {
        auto evalState = std::make_shared<EvalState>(searchPath, store);
        nix::Flake flake = nix::getFlake(*evalState, FlakeRef(flakeUri), true);
        printFlakeInfo(flake, json);
    }
};

struct CmdFlakeAdd : MixEvalArgs, Command
{
    FlakeUri alias;
    FlakeUri uri;

    std::string name() override
    {
        return "add";
    }

    std::string description() override
    {
        return "upsert flake in user flake registry";
    }

    CmdFlakeAdd()
    {
        expectArg("alias", &alias);
        expectArg("flake-uri", &uri);
        evalSettings.pureEval = false; // FIXME
    }

    void run() override
    {
        FlakeRef aliasRef(alias);
        Path userRegistryPath = getUserRegistryPath();
        auto userRegistry = readRegistry(userRegistryPath);
        userRegistry->entries.erase(aliasRef);
        userRegistry->entries.insert_or_assign(aliasRef, FlakeRef(uri));
        writeRegistry(*userRegistry, userRegistryPath);
    }
};

struct CmdFlakeRemove : virtual Args, MixEvalArgs, Command
{
    FlakeUri alias;

    std::string name() override
    {
        return "remove";
    }

    std::string description() override
    {
        return "remove flake from user flake registry";
    }

    CmdFlakeRemove()
    {
        expectArg("alias", &alias);
        evalSettings.pureEval = false; // FIXME
    }

    void run() override
    {
        Path userRegistryPath = getUserRegistryPath();
        auto userRegistry = readRegistry(userRegistryPath);
        userRegistry->entries.erase(FlakeRef(alias));
        writeRegistry(*userRegistry, userRegistryPath);
    }
};

struct CmdFlakePin : virtual Args, StoreCommand, MixEvalArgs
{
    FlakeUri alias;

    std::string name() override
    {
        return "pin";
    }

    std::string description() override
    {
        return "pin flake require in user flake registry";
    }

    CmdFlakePin()
    {
        expectArg("alias", &alias);
        evalSettings.pureEval = false; // FIXME
    }

    void run(nix::ref<nix::Store> store) override
    {
        auto evalState = std::make_shared<EvalState>(searchPath, store);

        Path userRegistryPath = getUserRegistryPath();
        FlakeRegistry userRegistry = *readRegistry(userRegistryPath);
        auto it = userRegistry.entries.find(FlakeRef(alias));
        if (it != userRegistry.entries.end()) {
            it->second = getFlake(*evalState, it->second, true).ref;
            // The 'ref' in 'flake' is immutable.
            writeRegistry(userRegistry, userRegistryPath);
        } else {
            std::shared_ptr<FlakeRegistry> globalReg = getGlobalRegistry();
            it = globalReg->entries.find(FlakeRef(alias));
            if (it != globalReg->entries.end()) {
                FlakeRef newRef = getFlake(*evalState, it->second, true).ref;
                userRegistry.entries.insert_or_assign(alias, newRef);
                writeRegistry(userRegistry, userRegistryPath);
            } else
                throw Error("the flake alias '%s' does not exist in the user or global registry", alias);
        }
    }
};

struct CmdFlakeInit : virtual Args, Command
{
    std::string name() override
    {
        return "init";
    }

    std::string description() override
    {
        return "create a skeleton 'flake.nix' file in the current directory";
    }

    void run() override
    {
        Path flakeDir = absPath(".");

        if (!pathExists(flakeDir + "/.git"))
            throw Error("the directory '%s' is not a Git repository", flakeDir);

        Path flakePath = flakeDir + "/flake.nix";

        if (pathExists(flakePath))
            throw Error("file '%s' already exists", flakePath);

        writeFile(flakePath,
#include "flake-template.nix.gen.hh"
            );
    }
};

struct CmdFlake : virtual MultiCommand, virtual Command
{
    CmdFlake()
        : MultiCommand({make_ref<CmdFlakeList>()
            , make_ref<CmdFlakeUpdate>()
            , make_ref<CmdFlakeInfo>()
            , make_ref<CmdFlakeDeps>()
            , make_ref<CmdFlakeAdd>()
            , make_ref<CmdFlakeRemove>()
            , make_ref<CmdFlakePin>()
            , make_ref<CmdFlakeInit>()
          })
    {
    }

    std::string name() override
    {
        return "flake";
    }

    std::string description() override
    {
        return "manage Nix flakes";
    }

    void run() override
    {
        if (!command)
            throw UsageError("'nix flake' requires a sub-command.");
        command->run();
    }

    void printHelp(const string & programName, std::ostream & out) override
    {
        MultiCommand::printHelp(programName, out);
    }
};

static RegisterCommand r1(make_ref<CmdFlake>());
<|endoftext|>


<commit_before>#include <Python.h>
#include "platecapi.hpp"

static PyObject * platec_create(PyObject *self, PyObject *args)
{
    unsigned int seed;
    unsigned int width;
    unsigned int height;
    float sea_level;
    unsigned int erosion_period;
    float folding_ratio;
    unsigned int aggr_overlap_abs;
    float aggr_overlap_rel;
    unsigned int cycle_count;
    unsigned int num_plates;
    if (!PyArg_ParseTuple(args, "IIIfIfIfII", &seed, &width, &height, &sea_level, &erosion_period,
            &folding_ratio, &aggr_overlap_abs, &aggr_overlap_rel,
            &cycle_count, &num_plates))
        return NULL;
    srand(seed);

    void *litho = platec_api_create(seed, width, height, sea_level, erosion_period,
            folding_ratio, aggr_overlap_abs, aggr_overlap_rel,
            cycle_count, num_plates);

    long pointer = (long)litho;
    return Py_BuildValue("l", pointer);
}

static PyObject * platec_step(PyObject *self, PyObject *args)
{
    void *litho;
    if (!PyArg_ParseTuple(args, "l", &litho))
        return NULL;
    platec_api_step(litho);
    return Py_BuildValue("i", 0);
}

static PyObject * platec_destroy(PyObject *self, PyObject *args)
{
    void *litho;
    if (!PyArg_ParseTuple(args, "l", &litho))
        return NULL;
    platec_api_destroy(litho);
    return Py_BuildValue("i", 0);
}

PyObject *makelist(float array[], size_t size) {
    PyObject *l = PyList_New(size);
    for (size_t i = 0; i != size; ++i) {
        PyList_SET_ITEM(l, i, Py_BuildValue("f",array[i]));
    }
    return l;
}

static PyObject * platec_get_heightmap(PyObject *self, PyObject *args)
{
    size_t id;
    void *litho;
    if (!PyArg_ParseTuple(args, "l", &litho))
        return NULL;
    float *hm = platec_api_get_heightmap(litho);

    size_t width = lithosphere_getMapWidth(litho);
    size_t height = lithosphere_getMapHeight(litho);

    PyObject* res =  makelist(hm,width*height);
    Py_INCREF(res);
    return res;
}

static PyObject * platec_is_finished(PyObject *self, PyObject *args)
{
    size_t id;
    void *litho;
    if (!PyArg_ParseTuple(args, "l", &litho))
        return NULL;
    PyObject* res = Py_BuildValue("b",platec_api_is_finished(litho));
    return res;
}

static PyMethodDef PlatecMethods[] = {
    {"create",  platec_create, METH_VARARGS,
     "Create initial plates configuration."},
    {"destroy",  platec_destroy, METH_VARARGS,
     "Release the data for the simulation."},
    {"get_heightmap",  platec_get_heightmap, METH_VARARGS,
     "Get current heightmap."},
    {"step", platec_step, METH_VARARGS,
     "Perform next step of the simulation."},
    {"is_finished",  platec_is_finished, METH_VARARGS,
     "Is the simulation finished?"},
    {NULL, NULL, 0, NULL} /* Sentinel */
};

#if PY_MAJOR_VERSION >= 3
    static struct PyModuleDef moduledef = {
        PyModuleDef_HEAD_INIT,
        "platec",     /* m_name */
        "Plate tectonics simulation",  /* m_doc */
        -1,                  /* m_size */
        PlatecMethods,       /* m_methods */
        NULL,                /* m_reload */
        NULL,                /* m_traverse */
        NULL,                /* m_clear */
        NULL,                /* m_free */
    };
#endif

#if PY_MAJOR_VERSION >= 3
    #define MOD_INIT(name) PyMODINIT_FUNC PyInit_##name(void)
#else
    #define MOD_INIT(name) PyMODINIT_FUNC init##name(void)
#endif

MOD_INIT(platec)
{
    #if PY_MAJOR_VERSION >= 3
        PyMODINIT_FUNC m = PyModule_Create(&moduledef);
        return m;
    #else
        Py_InitModule3("platec",
            PlatecMethods, "Plate tectonics simulation");
    #endif
}
<commit_msg>module: fixing return statement for python 3<commit_after>#include <Python.h>
#include "platecapi.hpp"

static PyObject * platec_create(PyObject *self, PyObject *args)
{
    unsigned int seed;
    unsigned int width;
    unsigned int height;
    float sea_level;
    unsigned int erosion_period;
    float folding_ratio;
    unsigned int aggr_overlap_abs;
    float aggr_overlap_rel;
    unsigned int cycle_count;
    unsigned int num_plates;
    if (!PyArg_ParseTuple(args, "IIIfIfIfII", &seed, &width, &height, &sea_level, &erosion_period,
            &folding_ratio, &aggr_overlap_abs, &aggr_overlap_rel,
            &cycle_count, &num_plates))
        return NULL;
    srand(seed);

    void *litho = platec_api_create(seed, width, height, sea_level, erosion_period,
            folding_ratio, aggr_overlap_abs, aggr_overlap_rel,
            cycle_count, num_plates);

    long pointer = (long)litho;
    return Py_BuildValue("l", pointer);
}

static PyObject * platec_step(PyObject *self, PyObject *args)
{
    void *litho;
    if (!PyArg_ParseTuple(args, "l", &litho))
        return NULL;
    platec_api_step(litho);
    return Py_BuildValue("i", 0);
}

static PyObject * platec_destroy(PyObject *self, PyObject *args)
{
    void *litho;
    if (!PyArg_ParseTuple(args, "l", &litho))
        return NULL;
    platec_api_destroy(litho);
    return Py_BuildValue("i", 0);
}

PyObject *makelist(float array[], size_t size) {
    PyObject *l = PyList_New(size);
    for (size_t i = 0; i != size; ++i) {
        PyList_SET_ITEM(l, i, Py_BuildValue("f",array[i]));
    }
    return l;
}

static PyObject * platec_get_heightmap(PyObject *self, PyObject *args)
{
    size_t id;
    void *litho;
    if (!PyArg_ParseTuple(args, "l", &litho))
        return NULL;
    float *hm = platec_api_get_heightmap(litho);

    size_t width = lithosphere_getMapWidth(litho);
    size_t height = lithosphere_getMapHeight(litho);

    PyObject* res =  makelist(hm,width*height);
    Py_INCREF(res);
    return res;
}

static PyObject * platec_is_finished(PyObject *self, PyObject *args)
{
    size_t id;
    void *litho;
    if (!PyArg_ParseTuple(args, "l", &litho))
        return NULL;
    PyObject* res = Py_BuildValue("b",platec_api_is_finished(litho));
    return res;
}

static PyMethodDef PlatecMethods[] = {
    {"create",  platec_create, METH_VARARGS,
     "Create initial plates configuration."},
    {"destroy",  platec_destroy, METH_VARARGS,
     "Release the data for the simulation."},
    {"get_heightmap",  platec_get_heightmap, METH_VARARGS,
     "Get current heightmap."},
    {"step", platec_step, METH_VARARGS,
     "Perform next step of the simulation."},
    {"is_finished",  platec_is_finished, METH_VARARGS,
     "Is the simulation finished?"},
    {NULL, NULL, 0, NULL} /* Sentinel */
};

#if PY_MAJOR_VERSION >= 3
    static struct PyModuleDef moduledef = {
        PyModuleDef_HEAD_INIT,
        "platec",     /* m_name */
        "Plate tectonics simulation",  /* m_doc */
        -1,                  /* m_size */
        PlatecMethods,       /* m_methods */
        NULL,                /* m_reload */
        NULL,                /* m_traverse */
        NULL,                /* m_clear */
        NULL,                /* m_free */
    };
#endif

#if PY_MAJOR_VERSION >= 3
    #define MOD_INIT(name) PyMODINIT_FUNC PyInit_##name(void)
#else
    #define MOD_INIT(name) PyMODINIT_FUNC init##name(void)
#endif

MOD_INIT(platec)
{
    #if PY_MAJOR_VERSION >= 3
        return PyModule_Create(&moduledef);
    #else
        Py_InitModule3("platec",
            PlatecMethods, "Plate tectonics simulation");
    #endif
}
<|endoftext|>


<commit_before>// -*- C++ -*-
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++98, c++03, c++11, c++14
// XFAIL: dylib-has-no-bad_variant_access && !libcpp-no-exceptions

// <variant>

// template <class ...Types> class variant;

// template <class T> constexpr variant(T&&) noexcept(see below);

#include <cassert>
#include <string>
#include <type_traits>
#include <variant>
#include <memory>

#include "test_macros.h"
#include "variant_test_helpers.hpp"

struct Dummy {
  Dummy() = default;
};

struct ThrowsT {
  ThrowsT(int) noexcept(false) {}
};

struct NoThrowT {
  NoThrowT(int) noexcept(true) {}
};

struct AnyConstructible { template <typename T> AnyConstructible(T&&) {} };
struct NoConstructible { NoConstructible() = delete; };
template <class T>
struct RValueConvertibleFrom { RValueConvertibleFrom(T&&) {} };

void test_T_ctor_noexcept() {
  {
    using V = std::variant<Dummy, NoThrowT>;
    static_assert(std::is_nothrow_constructible<V, int>::value, "");
  }
  {
    using V = std::variant<Dummy, ThrowsT>;
    static_assert(!std::is_nothrow_constructible<V, int>::value, "");
  }
}

void test_T_ctor_sfinae() {
  {
    using V = std::variant<long, long long>;
    static_assert(!std::is_constructible<V, int>::value, "ambiguous");
  }
  {
    using V = std::variant<std::string, std::string>;
    static_assert(!std::is_constructible<V, const char *>::value, "ambiguous");
  }
  {
    using V = std::variant<std::string, void *>;
    static_assert(!std::is_constructible<V, int>::value,
                  "no matching constructor");
  }
  {
    using V = std::variant<std::string, float>;
    static_assert(std::is_constructible<V, int>::value == VariantAllowsNarrowingConversions,
                  "no matching constructor");
  }
  {
    using V = std::variant<std::unique_ptr<int>, bool>;
    static_assert(!std::is_constructible<V, std::unique_ptr<char>>::value,
                  "no explicit bool in constructor");
    struct X {
      operator void*();
    };
    static_assert(!std::is_constructible<V, X>::value,
                  "no boolean conversion in constructor");
    static_assert(!std::is_constructible<V, std::false_type>::value,
                  "no converted to bool in constructor");
  }
  {
    struct X {};
    struct Y {
      operator X();
    };
    using V = std::variant<X>;
    static_assert(std::is_constructible<V, Y>::value,
                  "regression on user-defined conversions in constructor");
  }
  {
    using V = std::variant<AnyConstructible, NoConstructible>;
    static_assert(
        !std::is_constructible<V, std::in_place_type_t<NoConstructible>>::value,
        "no matching constructor");
    static_assert(!std::is_constructible<V, std::in_place_index_t<1>>::value,
                  "no matching constructor");
  }

#if !defined(TEST_VARIANT_HAS_NO_REFERENCES)
  {
    using V = std::variant<int, int &&>;
    static_assert(!std::is_constructible<V, int>::value, "ambiguous");
  }
  {
    using V = std::variant<int, const int &>;
    static_assert(!std::is_constructible<V, int>::value, "ambiguous");
  }
#endif
}

void test_T_ctor_basic() {
  {
    constexpr std::variant<int> v(42);
    static_assert(v.index() == 0, "");
    static_assert(std::get<0>(v) == 42, "");
  }
  {
    constexpr std::variant<int, long> v(42l);
    static_assert(v.index() == 1, "");
    static_assert(std::get<1>(v) == 42, "");
  }
#ifndef TEST_VARIANT_ALLOWS_NARROWING_CONVERSIONS
  {
    constexpr std::variant<unsigned, long> v(42);
    static_assert(v.index() == 1, "");
    static_assert(std::get<1>(v) == 42, "");
  }
#endif
  {
    std::variant<std::string, bool const> v = "foo";
    assert(v.index() == 0);
    assert(std::get<0>(v) == "foo");
  }
  {
    std::variant<bool volatile, std::unique_ptr<int>> v = nullptr;
    assert(v.index() == 1);
    assert(std::get<1>(v) == nullptr);
  }
  {
    std::variant<bool volatile const, int> v = true;
    assert(v.index() == 0);
    assert(std::get<0>(v));
  }
  {
    std::variant<RValueConvertibleFrom<int>> v1 = 42;
    assert(v1.index() == 0);

    int x = 42;
    std::variant<RValueConvertibleFrom<int>, AnyConstructible> v2 = x;
    assert(v2.index() == 1);
  }
#if !defined(TEST_VARIANT_HAS_NO_REFERENCES)
  {
    using V = std::variant<const int &, int &&, long>;
    static_assert(std::is_convertible<int &, V>::value, "must be implicit");
    int x = 42;
    V v(x);
    assert(v.index() == 0);
    assert(&std::get<0>(v) == &x);
  }
  {
    using V = std::variant<const int &, int &&, long>;
    static_assert(std::is_convertible<int, V>::value, "must be implicit");
    int x = 42;
    V v(std::move(x));
    assert(v.index() == 1);
    assert(&std::get<1>(v) == &x);
  }
#endif
}

struct BoomOnAnything {
  template <class T>
  constexpr BoomOnAnything(T) { static_assert(!std::is_same<T, T>::value, ""); }
};

void test_no_narrowing_check_for_class_types() {
  using V = std::variant<int, BoomOnAnything>;
  V v(42);
  assert(v.index() == 0);
  assert(std::get<0>(v) == 42);
}

int main(int, char**) {
  test_T_ctor_basic();
  test_T_ctor_noexcept();
  test_T_ctor_sfinae();
  test_no_narrowing_check_for_class_types();
  return 0;
}
<commit_msg>Add test for variant construction with duplicate types.<commit_after>// -*- C++ -*-
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++98, c++03, c++11, c++14
// XFAIL: dylib-has-no-bad_variant_access && !libcpp-no-exceptions

// <variant>

// template <class ...Types> class variant;

// template <class T> constexpr variant(T&&) noexcept(see below);

#include <cassert>
#include <string>
#include <type_traits>
#include <variant>
#include <memory>

#include "test_macros.h"
#include "variant_test_helpers.hpp"

struct Dummy {
  Dummy() = default;
};

struct ThrowsT {
  ThrowsT(int) noexcept(false) {}
};

struct NoThrowT {
  NoThrowT(int) noexcept(true) {}
};

struct AnyConstructible { template <typename T> AnyConstructible(T&&) {} };
struct NoConstructible { NoConstructible() = delete; };
template <class T>
struct RValueConvertibleFrom { RValueConvertibleFrom(T&&) {} };

void test_T_ctor_noexcept() {
  {
    using V = std::variant<Dummy, NoThrowT>;
    static_assert(std::is_nothrow_constructible<V, int>::value, "");
  }
  {
    using V = std::variant<Dummy, ThrowsT>;
    static_assert(!std::is_nothrow_constructible<V, int>::value, "");
  }
}

void test_T_ctor_sfinae() {
  {
    using V = std::variant<long, long long>;
    static_assert(!std::is_constructible<V, int>::value, "ambiguous");
  }
  {
    using V = std::variant<std::string, std::string>;
    static_assert(!std::is_constructible<V, const char *>::value, "ambiguous");
  }
  {
    using V = std::variant<std::string, void *>;
    static_assert(!std::is_constructible<V, int>::value,
                  "no matching constructor");
  }
  {
    using V = std::variant<std::string, float>;
    static_assert(std::is_constructible<V, int>::value == VariantAllowsNarrowingConversions,
                  "no matching constructor");
  }
  {
    using V = std::variant<std::unique_ptr<int>, bool>;
    static_assert(!std::is_constructible<V, std::unique_ptr<char>>::value,
                  "no explicit bool in constructor");
    struct X {
      operator void*();
    };
    static_assert(!std::is_constructible<V, X>::value,
                  "no boolean conversion in constructor");
    static_assert(!std::is_constructible<V, std::false_type>::value,
                  "no converted to bool in constructor");
  }
  {
    struct X {};
    struct Y {
      operator X();
    };
    using V = std::variant<X>;
    static_assert(std::is_constructible<V, Y>::value,
                  "regression on user-defined conversions in constructor");
  }
  {
    using V = std::variant<AnyConstructible, NoConstructible>;
    static_assert(
        !std::is_constructible<V, std::in_place_type_t<NoConstructible>>::value,
        "no matching constructor");
    static_assert(!std::is_constructible<V, std::in_place_index_t<1>>::value,
                  "no matching constructor");
  }

#if !defined(TEST_VARIANT_HAS_NO_REFERENCES)
  {
    using V = std::variant<int, int &&>;
    static_assert(!std::is_constructible<V, int>::value, "ambiguous");
  }
  {
    using V = std::variant<int, const int &>;
    static_assert(!std::is_constructible<V, int>::value, "ambiguous");
  }
#endif
}

void test_T_ctor_basic() {
  {
    constexpr std::variant<int> v(42);
    static_assert(v.index() == 0, "");
    static_assert(std::get<0>(v) == 42, "");
  }
  {
    constexpr std::variant<int, long> v(42l);
    static_assert(v.index() == 1, "");
    static_assert(std::get<1>(v) == 42, "");
  }
#ifndef TEST_VARIANT_ALLOWS_NARROWING_CONVERSIONS
  {
    constexpr std::variant<unsigned, long> v(42);
    static_assert(v.index() == 1, "");
    static_assert(std::get<1>(v) == 42, "");
  }
#endif
  {
    std::variant<std::string, bool const> v = "foo";
    assert(v.index() == 0);
    assert(std::get<0>(v) == "foo");
  }
  {
    std::variant<bool volatile, std::unique_ptr<int>> v = nullptr;
    assert(v.index() == 1);
    assert(std::get<1>(v) == nullptr);
  }
  {
    std::variant<bool volatile const, int> v = true;
    assert(v.index() == 0);
    assert(std::get<0>(v));
  }
  {
    std::variant<RValueConvertibleFrom<int>> v1 = 42;
    assert(v1.index() == 0);

    int x = 42;
    std::variant<RValueConvertibleFrom<int>, AnyConstructible> v2 = x;
    assert(v2.index() == 1);
  }
#if !defined(TEST_VARIANT_HAS_NO_REFERENCES)
  {
    using V = std::variant<const int &, int &&, long>;
    static_assert(std::is_convertible<int &, V>::value, "must be implicit");
    int x = 42;
    V v(x);
    assert(v.index() == 0);
    assert(&std::get<0>(v) == &x);
  }
  {
    using V = std::variant<const int &, int &&, long>;
    static_assert(std::is_convertible<int, V>::value, "must be implicit");
    int x = 42;
    V v(std::move(x));
    assert(v.index() == 1);
    assert(&std::get<1>(v) == &x);
  }
#endif
}

struct BoomOnAnything {
  template <class T>
  constexpr BoomOnAnything(T) { static_assert(!std::is_same<T, T>::value, ""); }
};

void test_no_narrowing_check_for_class_types() {
  using V = std::variant<int, BoomOnAnything>;
  V v(42);
  assert(v.index() == 0);
  assert(std::get<0>(v) == 42);
}

struct Bar {};
struct Baz {};
void test_construction_with_repeated_types() {
  using V = std::variant<int, Bar, Baz, int, Baz, int, int>;
  static_assert(!std::is_constructible<V, int>::value, "");
  static_assert(!std::is_constructible<V, Baz>::value, "");
  // OK, the selected type appears only once and so it shouldn't
  // be affected by the duplicate types.
  static_assert(std::is_constructible<V, Bar>::value, "");
}

int main(int, char**) {
  test_T_ctor_basic();
  test_T_ctor_noexcept();
  test_T_ctor_sfinae();
  test_no_narrowing_check_for_class_types();
  test_construction_with_repeated_types();
  return 0;
}
<|endoftext|>


<commit_before>#include "buzz_qt.h"

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

CBuzzQT::CBuzzQT() {
   RegisterUserFunction<CBuzzQT,CFootBotEntity>(&CBuzzQT::Draw);
   RegisterUserFunction<CBuzzQT,CSpiriEntity>(&CBuzzQT::Draw);
}

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

void CBuzzQT::Draw(CFootBotEntity& c_entity) {
   Draw(dynamic_cast<CBuzzController&>(c_entity.GetControllableEntity().GetController()));
}

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

void CBuzzQT::Draw(CSpiriEntity& c_entity) {
   Draw(dynamic_cast<CBuzzController&>(c_entity.GetControllableEntity().GetController()));
}

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

void CBuzzQT::Draw(CBuzzController& c_contr) {
   /* This is the message that will be shown */
   std::string strMsg("R" + ToString(c_contr.GetBuzzVM()->robot));
   /* Get 'debug' variable */
   buzzvm_pushs(c_contr.GetBuzzVM(),
                buzzvm_string_register(c_contr.GetBuzzVM(), "debug"));
   buzzvm_gload(c_contr.GetBuzzVM());
   if(buzzvm_stack_at(c_contr.GetBuzzVM(), 1)->o.type == BUZZTYPE_STRING) {
      strMsg += ": " + std::string(buzzvm_stack_at(c_contr.GetBuzzVM(), 1)->s.value.str);
   }
   /* Disable face culling to be sure the text is visible from anywhere */
   glDisable(GL_CULL_FACE);
   /* Disable lighting, so it does not interfere with the chosen text color */
   glDisable(GL_LIGHTING);
   /* Set the text color */
   CColor cColor(CColor::BLACK);
   glColor3ub(cColor.GetRed(), cColor.GetGreen(), cColor.GetBlue());
   /* The position of the text is expressed wrt the reference point of
    * the robot */
   GetOpenGLWidget().renderText(0.0, 0.0, 0.4,   // position
                                strMsg.c_str()); // text
   /* Restore lighting */
   glEnable(GL_LIGHTING);
   /* Restore face culling */
   glEnable(GL_CULL_FACE);
}

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

REGISTER_QTOPENGL_USER_FUNCTIONS(CBuzzQT, "buzz_qt")
<commit_msg>Fixed robot debug display.<commit_after>#include "buzz_qt.h"

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

CBuzzQT::CBuzzQT() {
   RegisterUserFunction<CBuzzQT,CFootBotEntity>(&CBuzzQT::Draw);
   RegisterUserFunction<CBuzzQT,CSpiriEntity>(&CBuzzQT::Draw);
}

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

void CBuzzQT::Draw(CFootBotEntity& c_entity) {
   Draw(dynamic_cast<CBuzzController&>(c_entity.GetControllableEntity().GetController()));
}

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

void CBuzzQT::Draw(CSpiriEntity& c_entity) {
   Draw(dynamic_cast<CBuzzController&>(c_entity.GetControllableEntity().GetController()));
}

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

void CBuzzQT::Draw(CBuzzController& c_contr) {
   /* This is the message that will be shown */
   std::string strMsg("R" + ToString(c_contr.GetBuzzVM()->robot));
   /* Append debug message */
   if(c_contr.GetDebugMsg() != "")
      strMsg += ": " + c_contr.GetDebugMsg();
   /* Disable face culling to be sure the text is visible from anywhere */
   glDisable(GL_CULL_FACE);
   /* Disable lighting, so it does not interfere with the chosen text color */
   glDisable(GL_LIGHTING);
   /* Set the text color */
   CColor cColor(CColor::BLACK);
   glColor3ub(cColor.GetRed(), cColor.GetGreen(), cColor.GetBlue());
   /* The position of the text is expressed wrt the reference point of
    * the robot */
   GetOpenGLWidget().renderText(0.0, 0.0, 0.4,   // position
                                strMsg.c_str()); // text
   /* Restore lighting */
   glEnable(GL_LIGHTING);
   /* Restore face culling */
   glEnable(GL_CULL_FACE);
}

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

REGISTER_QTOPENGL_USER_FUNCTIONS(CBuzzQT, "buzz_qt")
<|endoftext|>


<commit_before>/* bzflag
 * Copyright (c) 1993 - 2004 Tim Riker
 *
 * This package is free software;  you can redistribute it and/or
 * modify it under the terms of the license found in the file
 * named COPYING that should have accompanied this file.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

#include "CursesMenu.h"
#include "StateDatabase.h"
#include "TextUtils.h"

CursesMenuItem::CursesMenuItem(const std::string& str) : text(str) {

}

CursesMenuItem::~CursesMenuItem() {

}

void CursesMenuItem::showItem(WINDOW* menuWin, int line, int col, int width,
			      bool selected) {
  /* just print the text of the item centered between col and col+width
     if this item is selected, use reverse video */
  wmove(menuWin, line, col);
  if (selected)
    wattron(menuWin, A_REVERSE);
  for (unsigned int i = 0; i < (width - text.size()) / 2; ++i)
    waddstr(menuWin, " ");
  waddstr(menuWin, text.c_str());
  for (int i = (width - text.size()) /2 + text.size(); i < width; ++i)
    waddstr(menuWin, " ");
  if (selected)
    wattroff(menuWin, A_REVERSE);
}

bool CursesMenuItem::handleKey(int, std::string&, CursesMenu&) {
  return false;
}

void CursesMenuItem::deselect() {

}

SubmenuCMItem::SubmenuCMItem(const std::string& str, MenuCallback callback)
  : CursesMenuItem(str), cb(callback) {

}

bool SubmenuCMItem::handleKey(int c, std::string&, CursesMenu& menu) {
  // different key codes for the enter key
  if (c == '\n' || c == 13) {
    menu.setUpdateCallback(cb);
    menu.forceUpdate();
  }
  return false;
}

CallbackCMItem::CallbackCMItem(const std::string& str, MenuCallback callback)
  : CursesMenuItem(str), cb(callback) {

}

bool CallbackCMItem::handleKey(int c, std::string&, CursesMenu& menu) {
  // different key codes for the enter key
  if (c == '\n' || c == 13) {
    cb(menu);
  }
  return false;
}

CommandCMItem::CommandCMItem(const std::string& str, const std::string& cmd,
			     bool update)
  : CursesMenuItem(str), command(cmd), forceUpdate(update) {

}

bool CommandCMItem::handleKey(int c, std::string& str, CursesMenu& menu) {
  // different key codes for the enter key
  if (c == '\n' || c == 13) {
    str = command;
    if (forceUpdate)
      menu.forceUpdate();
    return true;
  }
  return false;
}

BoolCMItem::BoolCMItem(std::string name, bool& variable,  std::string trueText,
			  std::string falseText)
  : CursesMenuItem(name), varRef(variable), trueTxt(trueText),
    falseTxt(falseText) {

}

void BoolCMItem::showItem(WINDOW* menuWin, int line, int col, int width,
			  bool selected) {
  /* print the name of the variable to the left of the center and the
     value to the right, use reverse video if it is selected */
  wmove(menuWin, line, col);
  if (selected)
    wattron(menuWin, A_REVERSE);
  for (unsigned int i = 0; i < width / 2 - text.size() - 1; ++i)
    waddstr(menuWin, " ");
  waddstr(menuWin, text.c_str());
  wmove(menuWin, line, col + width / 2 + 1);
  std::string& value = (varRef ? trueTxt : falseTxt);
  waddstr(menuWin, value.c_str());
  for (int i = width / 2 + 1 + value.size(); i < width; ++i)
    waddstr(menuWin, " ");
  if (selected)
    wattroff(menuWin, A_REVERSE);
}

bool BoolCMItem::handleKey(int c, std::string&, CursesMenu&) {
  if (c == ' ')
    varRef = !varRef;
  return false;
}

BZDBCMItem::BZDBCMItem(const std::string& variable)
  : CursesMenuItem(variable), editing(false) {

}

void BZDBCMItem::showItem(WINDOW* menuWin, int line, int col, int width,
			  bool selected) {
  /* print the name of the variable to the left of the center and the
     value to the right, use reverse video if it is selected, but don't use
     reverse video for the value if we're editing it */
  wmove(menuWin, line, col);
  if (selected)
    wattron(menuWin, A_REVERSE);
  for (unsigned int i = 0; i < width / 2 - text.size() - 1; ++i)
    waddstr(menuWin, " ");
  waddstr(menuWin, text.c_str());
  wmove(menuWin, line, col + width / 2 + 1);
  std::string value = BZDB.get(text);
  if (editing) {
    wattroff(menuWin, A_REVERSE);
    waddstr(menuWin, editString.c_str());
    waddstr(menuWin, "_");
  }
  else
    waddstr(menuWin, value.c_str());
  for (int i = width / 2 + 1 + value.size(); i < width; ++i)
    waddstr(menuWin, " ");
  if (selected && !editing)
    wattroff(menuWin, A_REVERSE);
}

bool BZDBCMItem::handleKey(int c, std::string& str, CursesMenu&) {
  // if we're not editing, start if we get a return key
  if (!editing && (c == '\n' || c == 13)) {
    editing = true;
    editString = BZDB.get(text);
    return false;
  }

  // OK, we're editing
  switch (c) {

    // different codes for the return key - stop and send a /set command
  case '\n':
  case 13:
    editing = false;
    str = "/set ";
    str += text + " \"" + editString + "\"";
    return true;

    // ESC - stop editing, don't touch the BZDB value
  case 27:
    editing = false;
    break;

    // backspace/delete - delete the last character
  case KEY_BACKSPACE:
  case KEY_DC:
  case 127:
    editString = (editString.size() > 0 ?
		  editString.substr(0, editString.size() - 1) : editString);
    break;

    // valid characters - edit the string
  default:
    if (c < 32 || c > 127 || editString.size() > 30)
      return false;
    editString += char(c);
    break;
  }

  return false;
}

void BZDBCMItem::deselect() {
  editing = false;
}

PlayerCMItem::PlayerCMItem(const PlayerIdMap& players, PlayerId playerId)
  : CursesMenuItem(), playerMap(players), id(playerId) {

}

void PlayerCMItem::showItem(WINDOW* menuWin, int line, int col, int width,
			    bool selected) {
  // score (wins-losses)[tks] callsign IP, reverse video if selected
  std::string name, ip;
  int wins, losses, tks;
  PlayerIdMap::const_iterator iter = playerMap.find(id);
  if (iter != playerMap.end()) {
    name = iter->second.name;
    ip = iter->second.ip;
    wins = iter->second.wins;
    losses = iter->second.losses;
    tks = iter->second.tks;
    std::ostringstream oss;
    oss<<(wins - losses)<<" ("<<wins<<"-"<<losses<<")["<<tks<<"]";
    unsigned int scoreLength = oss.str().size();
    for (unsigned int i = 0; i < 21 - scoreLength; ++i)
      oss<<' ';
    oss<<name;
    for (unsigned int i = 0; i < CallSignLen + 1 - name.size(); ++i)
      oss<<' ';
    oss<<ip;
    for (unsigned int i = 0; i < 15 - ip.size(); ++i)
      oss<<' ';
    if (selected)
      wattron(menuWin, A_REVERSE);
    wmove(menuWin, line, col);
    waddstr(menuWin, oss.str().substr(0, width).c_str());
    if (selected)
      wattroff(menuWin, A_REVERSE);
  }
}

CursesMenu::CursesMenu(const PlayerIdMap& p)
  : players(p), dirty(true) {

}

CursesMenu::~CursesMenu() {
  clear();
}

void CursesMenu::setHeader(const std::string& newHeader) {
  header = newHeader;
}

void CursesMenu::addItem(CursesMenuItem* item) {
  items.push_back(item);
}

void CursesMenu::clear() {
  for (unsigned int i = 0; i < items.size(); ++i)
    delete items[i];
  items.clear();
  selection = 0;
}

void CursesMenu::showMenu() {
  if (window == NULL)
    return;
  werase(window);

  // update the menu if needed
  rebuild();

  // get the window size
  int x1, x2, y1, y2, w, h;
  getbegyx(window, y1, x1);
  getmaxyx(window, y2, x2);
  w = x2 - x1;
  h = y2 - y1;

  wmove(window, 0, w / 2 - header.size() / 2);
  waddstr(window, header.c_str());

  // this magic should scroll the menu so that the selected menu item
  // always is visible
  int start = selection - (h / 2 - 2);
  start = (start + (h - 3) > (signed)items.size() ?
	   items.size() - (h - 3) : start);
  start = (start < 0 ? 0 : start);
  int end = start + (h - 3);
  end = ((unsigned)end > items.size() ? items.size() : end);

  // show the menu items
  for (int i = start; i < end; ++i)
    items[i]->showItem(window, 2 + (i - start), 10, w - 20, i == selection);

  // draw a line at the bottom of the menu window
  wmove(window, h - 1, 0);
  wattron(window, A_UNDERLINE);
  for (int i = 0; i < COLS; ++i)
    waddstr(window, " ");
  wattroff(window, A_UNDERLINE);

  wrefresh(window);
}

bool CursesMenu::handleKey(int c, std::string& str) {
  bool result = false;
  str = "";
  switch (c) {
  case KEY_UP:
    items[selection]->deselect();
    selection = (selection == 0 ? items.size() - 1 : selection - 1);
    break;
  case KEY_DOWN:
    items[selection]->deselect();
    selection = ((unsigned)selection == items.size() - 1 ? 0 : selection + 1);
    break;
  default:
    result = items[selection]->handleKey(c, str, *this);
  }
  showMenu();
  return result;
}

void CursesMenu::setWindow(WINDOW* win) {
  window = win;
}

void CursesMenu::setUpdateCallback(MenuCallback cb) {
  rebuilder = cb;
}

void CursesMenu::forceUpdate() {
  dirty = true;
}

void CursesMenu::rebuild() {
  if (dirty) {
    dirty = false;
    rebuilder(*this);
  }
}

// Local Variables: ***
// mode:C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
<commit_msg>Clean up the playerlist output in the curses menu<commit_after>/* bzflag
 * Copyright (c) 1993 - 2004 Tim Riker
 *
 * This package is free software;  you can redistribute it and/or
 * modify it under the terms of the license found in the file
 * named COPYING that should have accompanied this file.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

#include "CursesMenu.h"
#include "StateDatabase.h"
#include "TextUtils.h"

CursesMenuItem::CursesMenuItem(const std::string& str) : text(str) {

}

CursesMenuItem::~CursesMenuItem() {

}

void CursesMenuItem::showItem(WINDOW* menuWin, int line, int col, int width,
			      bool selected) {
  /* just print the text of the item centered between col and col+width
     if this item is selected, use reverse video */
  wmove(menuWin, line, col);
  if (selected)
    wattron(menuWin, A_REVERSE);
  for (unsigned int i = 0; i < (width - text.size()) / 2; ++i)
    waddstr(menuWin, " ");
  waddstr(menuWin, text.c_str());
  for (int i = (width - text.size()) /2 + text.size(); i < width; ++i)
    waddstr(menuWin, " ");
  if (selected)
    wattroff(menuWin, A_REVERSE);
}

bool CursesMenuItem::handleKey(int, std::string&, CursesMenu&) {
  return false;
}

void CursesMenuItem::deselect() {

}

SubmenuCMItem::SubmenuCMItem(const std::string& str, MenuCallback callback)
  : CursesMenuItem(str), cb(callback) {

}

bool SubmenuCMItem::handleKey(int c, std::string&, CursesMenu& menu) {
  // different key codes for the enter key
  if (c == '\n' || c == 13) {
    menu.setUpdateCallback(cb);
    menu.forceUpdate();
  }
  return false;
}

CallbackCMItem::CallbackCMItem(const std::string& str, MenuCallback callback)
  : CursesMenuItem(str), cb(callback) {

}

bool CallbackCMItem::handleKey(int c, std::string&, CursesMenu& menu) {
  // different key codes for the enter key
  if (c == '\n' || c == 13) {
    cb(menu);
  }
  return false;
}

CommandCMItem::CommandCMItem(const std::string& str, const std::string& cmd,
			     bool update)
  : CursesMenuItem(str), command(cmd), forceUpdate(update) {

}

bool CommandCMItem::handleKey(int c, std::string& str, CursesMenu& menu) {
  // different key codes for the enter key
  if (c == '\n' || c == 13) {
    str = command;
    if (forceUpdate)
      menu.forceUpdate();
    return true;
  }
  return false;
}

BoolCMItem::BoolCMItem(std::string name, bool& variable,  std::string trueText,
			  std::string falseText)
  : CursesMenuItem(name), varRef(variable), trueTxt(trueText),
    falseTxt(falseText) {

}

void BoolCMItem::showItem(WINDOW* menuWin, int line, int col, int width,
			  bool selected) {
  /* print the name of the variable to the left of the center and the
     value to the right, use reverse video if it is selected */
  wmove(menuWin, line, col);
  if (selected)
    wattron(menuWin, A_REVERSE);
  for (unsigned int i = 0; i < width / 2 - text.size() - 1; ++i)
    waddstr(menuWin, " ");
  waddstr(menuWin, text.c_str());
  wmove(menuWin, line, col + width / 2 + 1);
  std::string& value = (varRef ? trueTxt : falseTxt);
  waddstr(menuWin, value.c_str());
  for (int i = width / 2 + 1 + value.size(); i < width; ++i)
    waddstr(menuWin, " ");
  if (selected)
    wattroff(menuWin, A_REVERSE);
}

bool BoolCMItem::handleKey(int c, std::string&, CursesMenu&) {
  if (c == ' ')
    varRef = !varRef;
  return false;
}

BZDBCMItem::BZDBCMItem(const std::string& variable)
  : CursesMenuItem(variable), editing(false) {

}

void BZDBCMItem::showItem(WINDOW* menuWin, int line, int col, int width,
			  bool selected) {
  /* print the name of the variable to the left of the center and the
     value to the right, use reverse video if it is selected, but don't use
     reverse video for the value if we're editing it */
  wmove(menuWin, line, col);
  if (selected)
    wattron(menuWin, A_REVERSE);
  for (unsigned int i = 0; i < width / 2 - text.size() - 1; ++i)
    waddstr(menuWin, " ");
  waddstr(menuWin, text.c_str());
  wmove(menuWin, line, col + width / 2 + 1);
  std::string value = BZDB.get(text);
  if (editing) {
    wattroff(menuWin, A_REVERSE);
    waddstr(menuWin, editString.c_str());
    waddstr(menuWin, "_");
  }
  else
    waddstr(menuWin, value.c_str());
  for (int i = width / 2 + 1 + value.size(); i < width; ++i)
    waddstr(menuWin, " ");
  if (selected && !editing)
    wattroff(menuWin, A_REVERSE);
}

bool BZDBCMItem::handleKey(int c, std::string& str, CursesMenu&) {
  // if we're not editing, start if we get a return key
  if (!editing && (c == '\n' || c == 13)) {
    editing = true;
    editString = BZDB.get(text);
    return false;
  }

  // OK, we're editing
  switch (c) {

    // different codes for the return key - stop and send a /set command
  case '\n':
  case 13:
    editing = false;
    str = "/set ";
    str += text + " \"" + editString + "\"";
    return true;

    // ESC - stop editing, don't touch the BZDB value
  case 27:
    editing = false;
    break;

    // backspace/delete - delete the last character
  case KEY_BACKSPACE:
  case KEY_DC:
  case 127:
    editString = (editString.size() > 0 ?
		  editString.substr(0, editString.size() - 1) : editString);
    break;

    // valid characters - edit the string
  default:
    if (c < 32 || c > 127 || editString.size() > 30)
      return false;
    editString += char(c);
    break;
  }

  return false;
}

void BZDBCMItem::deselect() {
  editing = false;
}

PlayerCMItem::PlayerCMItem(const PlayerIdMap& players, PlayerId playerId)
  : CursesMenuItem(), playerMap(players), id(playerId) {

}

void PlayerCMItem::showItem(WINDOW* menuWin, int line, int col, int width,
			    bool selected) {
  // score (wins-losses)[tks] callsign IP, reverse video if selected
  std::string name, ip;
  int wins, losses, tks;
  int scorePad = 17;
  int callsignPad = CallSignLen;
  PlayerIdMap::const_iterator iter = playerMap.find(id);
  if (iter != playerMap.end()) {
    name = iter->second.name;
    ip = iter->second.ip;
    wins = iter->second.wins;
    losses = iter->second.losses;
    tks = iter->second.tks;
    std::ostringstream oss;
    oss<<(wins - losses)<<" ("<<wins<<"-"<<losses<<")["<<tks<<"]";
    unsigned int streamLength = oss.str().size();
    for (unsigned int i = 0; i < scorePad - streamLength; ++i)
      oss<<' ';
    oss<<' '<<name;
    for (unsigned int i = 0; i < callsignPad - name.size(); ++i)
      oss<<' ';
    streamLength = oss.str().size();
    if (selected)
      wattron(menuWin, A_REVERSE);
    wmove(menuWin, line, col);
    waddstr(menuWin,
	    (oss.str().substr(0, width - ip.size() - 1) + " ").c_str());
    waddstr(menuWin, ip.c_str());
    if (selected)
      wattroff(menuWin, A_REVERSE);
  }
}

CursesMenu::CursesMenu(const PlayerIdMap& p)
  : players(p), dirty(true) {

}

CursesMenu::~CursesMenu() {
  clear();
}

void CursesMenu::setHeader(const std::string& newHeader) {
  header = newHeader;
}

void CursesMenu::addItem(CursesMenuItem* item) {
  items.push_back(item);
}

void CursesMenu::clear() {
  for (unsigned int i = 0; i < items.size(); ++i)
    delete items[i];
  items.clear();
  selection = 0;
}

void CursesMenu::showMenu() {
  if (window == NULL)
    return;
  werase(window);

  // update the menu if needed
  rebuild();

  // get the window size
  int x1, x2, y1, y2, w, h;
  getbegyx(window, y1, x1);
  getmaxyx(window, y2, x2);
  w = x2 - x1;
  h = y2 - y1;

  wmove(window, 0, w / 2 - header.size() / 2);
  waddstr(window, header.c_str());

  // this magic should scroll the menu so that the selected menu item
  // always is visible
  int start = selection - (h / 2 - 2);
  start = (start + (h - 3) > (signed)items.size() ?
	   items.size() - (h - 3) : start);
  start = (start < 0 ? 0 : start);
  int end = start + (h - 3);
  end = ((unsigned)end > items.size() ? items.size() : end);

  // show the menu items
  for (int i = start; i < end; ++i)
    items[i]->showItem(window, 2 + (i - start), 10, w - 20, i == selection);

  // draw a line at the bottom of the menu window
  wmove(window, h - 1, 0);
  wattron(window, A_UNDERLINE);
  for (int i = 0; i < COLS; ++i)
    waddstr(window, " ");
  wattroff(window, A_UNDERLINE);

  wrefresh(window);
}

bool CursesMenu::handleKey(int c, std::string& str) {
  bool result = false;
  str = "";
  switch (c) {
  case KEY_UP:
    items[selection]->deselect();
    selection = (selection == 0 ? items.size() - 1 : selection - 1);
    break;
  case KEY_DOWN:
    items[selection]->deselect();
    selection = ((unsigned)selection == items.size() - 1 ? 0 : selection + 1);
    break;
  default:
    result = items[selection]->handleKey(c, str, *this);
  }
  showMenu();
  return result;
}

void CursesMenu::setWindow(WINDOW* win) {
  window = win;
}

void CursesMenu::setUpdateCallback(MenuCallback cb) {
  rebuilder = cb;
}

void CursesMenu::forceUpdate() {
  dirty = true;
}

void CursesMenu::rebuild() {
  if (dirty) {
    dirty = false;
    rebuilder(*this);
  }
}

// Local Variables: ***
// mode:C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
<|endoftext|>