<commit_before>/*
Copyright (c) 2014 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.

Author: Leonardo de Moura
*/
#include "kernel/abstract.h"
#include "kernel/instantiate.h"
#include "kernel/inductive/inductive.h"
#include "library/locals.h"
#include "library/tactic/tactic.h"
#include "library/reducible.h"
#include "library/tactic/expr_to_tactic.h"

namespace lean {
class inversion_tac {
    environment const &           m_env;
    io_state const &              m_ios;
    proof_state const &           m_ps;
    name_generator                m_ngen;
    substitution                  m_subst;
    std::unique_ptr<type_checker> m_tc;

    unsigned                      m_nparams;
    unsigned                      m_nindices;
    unsigned                      m_nminors;
    declaration                   m_I_decl;
    declaration                   m_cases_on_decl;

    void init_inductive_info(name const & n) {
        m_nindices      = *inductive::get_num_indices(m_env, n);
        m_nparams       = *inductive::get_num_params(m_env, n);
        m_nminors       = *inductive::get_num_minor_premises(m_env, n);
        m_I_decl        = m_env.get(n);
        m_cases_on_decl = m_env.get({n, "cases_on"});
    }

    bool is_inversion_applicable(expr const & t) {
        buffer<expr> args;
        expr const & fn = get_app_args(t, args);
        if (!is_constant(fn))
            return false;
        if (!inductive::is_inductive_decl(m_env, const_name(fn)))
            return false;
        if (!m_env.find(name{const_name(fn), "cases_on"}) ||
            !m_env.find(name("eq")) || !m_env.find(name("heq")))
            return false;
        init_inductive_info(const_name(fn));
        if (args.size() != m_nindices + m_nparams)
            return false;
        return true;
    }

    pair<expr, expr> mk_eq(expr const & lhs, expr const & rhs) {
        expr lhs_type = m_tc->infer(lhs).first;
        expr rhs_type = m_tc->infer(rhs).first;
        level l       = sort_level(m_tc->ensure_type(lhs_type).first);
        constraint_seq cs;
        if (m_tc->is_def_eq(lhs_type, rhs_type, justification(), cs) && !cs) {
            return mk_pair(mk_app(mk_constant("eq", to_list(l)), lhs_type, lhs, rhs),
                           mk_app(mk_constant({"eq", "refl"}, to_list(l)), rhs_type, rhs));
        } else {
            return mk_pair(mk_app(mk_constant("heq", to_list(l)), lhs_type, lhs, rhs_type, rhs),
                           mk_app(mk_constant({"heq", "refl"}, to_list(l)), rhs_type, rhs));
        }
    }

    goal generalize_indices(goal const & g, expr const & h, expr const & h_type) {
        buffer<expr> hyps;
        g.get_hyps(hyps);
        expr m            = g.get_meta();
        expr m_type       = g.get_type();
        name h_new_name   = g.get_unused_name(local_pp_name(h));
        buffer<expr> I_args;
        expr const & I    = get_app_args(h_type, I_args);
        if (m_nindices > 0) {
            expr h_new_type  = mk_app(I, I_args.size() - m_nindices, I_args.data());
            expr d           = m_tc->whnf(m_tc->infer(h_new_type).first).first;
            unsigned eq_idx  = 1;
            name eq_prefix("H");
            buffer<expr> ts;
            buffer<expr> eqs;
            buffer<expr> refls;
            name t_prefix("t");
            unsigned nidx = 1;
            for (unsigned i = I_args.size() - m_nindices; i < I_args.size(); i++) {
                expr t_type = binding_domain(d);
                expr t      = mk_local(m_ngen.next(), g.get_unused_name(t_prefix, nidx), t_type, binder_info());
                expr const & index = I_args[i];
                pair<expr, expr> p = mk_eq(t, index);
                expr new_eq   = p.first;
                expr new_refl = p.second;
                eqs.push_back(mk_local(m_ngen.next(), g.get_unused_name(eq_prefix, eq_idx), new_eq, binder_info()));
                refls.push_back(new_refl);
                h_new_type  = mk_app(h_new_type, t);
                hyps.push_back(t);
                ts.push_back(t);
                d           = instantiate(binding_body(d), t);
            }
            expr h_new    = mk_local(m_ngen.next(), h_new_name, h_new_type, local_info(h));
            hyps.push_back(h_new);
            expr new_type = Pi(eqs, g.get_type());
            expr new_meta = mk_app(mk_metavar(m_ngen.next(), Pi(hyps, new_type)), hyps);
            goal new_g(new_meta, new_type);
            expr val      = g.abstract(mk_app(mk_app(mk_app(Fun(ts, Fun(h_new, new_meta)), m_nindices, I_args.end() - m_nindices), h), refls));
            m_subst.assign(g.get_name(), val);
            return new_g;
        } else {
            expr h_new    = mk_local(m_ngen.next(), h_new_name, h_type, local_info(h));
            hyps.push_back(h_new);
            expr new_meta = mk_app(mk_metavar(m_ngen.next(), Pi(hyps, g.get_type())), hyps);
            goal new_g(new_meta, g.get_type());
            expr val      = g.abstract(mk_app(new_meta, h));
            m_subst.assign(g.get_name(), val);
            return new_g;
        }
    }

    list<goal> apply_cases_on(goal const & g) {
        buffer<expr> hyps;
        g.get_hyps(hyps);
        expr const & h      = hyps.back();
        expr const & h_type = mlocal_type(h);
        buffer<expr> I_args;
        expr const & I      = get_app_args(h_type, I_args);
        expr g_type         = g.get_type();
        expr cases_on;
        if (length(m_cases_on_decl.get_univ_params()) != length(m_I_decl.get_univ_params())) {
            level g_lvl  = sort_level(m_tc->ensure_type(g_type).first);
            cases_on     = mk_constant({const_name(I), "cases_on"}, cons(g_lvl, const_levels(I)));
        } else {
            cases_on     = mk_constant({const_name(I), "cases_on"}, const_levels(I));
        }
        // add params
        cases_on          = mk_app(cases_on, m_nparams, I_args.data());
        // add type former
        expr type_former  = Fun(m_nindices, I_args.end() - m_nindices, g_type);
        cases_on          = mk_app(cases_on, type_former);
        // add indices
        cases_on          = mk_app(cases_on, m_nindices, I_args.end() - m_nindices);
        // add h
        cases_on          = mk_app(cases_on, h);
        buffer<expr> new_hyps;
        new_hyps.append(hyps.size() - m_nindices - 1, hyps.data());
        // add a subgoal for each minor premise of cases_on
        expr cases_on_type = m_tc->whnf(m_tc->infer(cases_on).first).first;
        buffer<goal> new_goals;
        for (unsigned i = 0; i < m_nminors; i++) {
            expr new_type = binding_domain(cases_on_type);
            expr new_meta = mk_app(mk_metavar(m_ngen.next(), Pi(new_hyps, new_type)), new_hyps);
            goal new_g(new_meta, new_type);
            new_goals.push_back(new_g);
            cases_on      = mk_app(cases_on, new_meta);
            cases_on_type = m_tc->whnf(binding_body(cases_on_type)).first; // the minor premises do not depend on each other
        }
        expr val        = g.abstract(cases_on);
        m_subst.assign(g.get_name(), val);
        return to_list(new_goals.begin(), new_goals.end());
    }

    // Store in \c r the number of arguments for each cases_on minor.
    void get_minors_nargs(buffer<unsigned> & r) {
        expr cases_on_type = m_cases_on_decl.get_type();
        for (unsigned i = 0; i < m_nparams + 1 + m_nindices + 1; i++)
            cases_on_type = binding_body(cases_on_type);
        for (unsigned i = 0; i < m_nminors; i++) {
            expr minor_type = binding_domain(cases_on_type);
            unsigned nargs  = 0;
            while (is_pi(minor_type)) {
                nargs++;
                minor_type = binding_body(minor_type);
            }
            r.push_back(nargs);
            cases_on_type   = binding_body(cases_on_type);
        }
    }

    list<goal> intros_minors_args(list<goal> gs) {
        buffer<unsigned> minors_nargs;
        get_minors_nargs(minors_nargs);
        lean_assert(length(gs) == minors_nargs.size());
        buffer<goal> new_gs;
        for (unsigned i = 0; i < minors_nargs.size(); i++) {
            goal const & g = head(gs);
            unsigned nargs = minors_nargs[i];
            buffer<expr> hyps;
            g.get_hyps(hyps);
            buffer<expr> new_hyps;
            new_hyps.append(hyps);
            expr g_type    = g.get_type();
            for (unsigned i = 0; i < nargs; i++) {
                expr type  = binding_domain(g_type);
                expr new_h = mk_local(m_ngen.next(), get_unused_name(binding_name(g_type), hyps), type, binder_info());
                new_hyps.push_back(new_h);
                g_type     = instantiate(binding_body(g_type), new_h);
            }
            g_type = head_beta_reduce(g_type);
            expr new_meta = mk_app(mk_metavar(m_ngen.next(), Pi(new_hyps, g_type)), new_hyps);
            goal new_g(new_meta, g_type);
            new_gs.push_back(new_g);
            expr val      = g.abstract(Fun(nargs, new_hyps.end() - nargs, new_meta));
            m_subst.assign(g.get_name(), val);
            gs = tail(gs);
        }
        return to_list(new_gs.begin(), new_gs.end());
    }

public:
    inversion_tac(environment const & env, io_state const & ios, proof_state const & ps):
        m_env(env), m_ios(ios), m_ps(ps),
        m_ngen(m_ps.get_ngen()),
        m_tc(mk_type_checker(m_env, m_ngen.mk_child(), m_ps.relax_main_opaque())) {
    }

    optional<proof_state> execute(name const & n) {
        goals const & gs  = m_ps.get_goals();
        if (empty(gs))
            return none_proof_state();
        goal  g           = head(gs);
        goals tail_gs     = tail(gs);
        auto p = g.find_hyp(n);
        if (!p)
            return none_proof_state();
        expr const & h    = p->first;
        expr h_type       = m_tc->whnf(mlocal_type(h)).first;
        if (!is_inversion_applicable(h_type))
            return none_proof_state();
        goal g1           = generalize_indices(g, h, h_type);
        list<goal> g2s    = apply_cases_on(g1);
        list<goal> g3s    = intros_minors_args(g2s);
        proof_state new_s(m_ps, append(g3s, tail_gs), m_subst, m_ngen);
        return some_proof_state(new_s);
    }
};

tactic inversion_tactic(name const & n) {
    auto fn = [=](environment const & env, io_state const & ios, proof_state const & ps) -> optional<proof_state> {
        inversion_tac tac(env, ios, ps);
        return tac.execute(n);
    };
    return tactic01(fn);
}

void initialize_inversion_tactic() {
    register_tac(name({"tactic", "inversion"}),
                 [](type_checker &, elaborate_fn const &, expr const & e, pos_info_provider const *) {
                     name n = tactic_expr_to_id(app_arg(e), "invalid 'inversion' tactic, argument must be an identifier");
                     return inversion_tactic(n);
                 });
}
void finalize_inversion_tactic() {}
}
<commit_msg>fix(library/tactic/inversion_tactic): inversion tactic for datatypes with dependent elimination<commit_after>/*
Copyright (c) 2014 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.

Author: Leonardo de Moura
*/
#include "kernel/abstract.h"
#include "kernel/instantiate.h"
#include "kernel/inductive/inductive.h"
#include "library/locals.h"
#include "library/tactic/tactic.h"
#include "library/reducible.h"
#include "library/tactic/expr_to_tactic.h"

namespace lean {
class inversion_tac {
    environment const &           m_env;
    io_state const &              m_ios;
    proof_state const &           m_ps;
    name_generator                m_ngen;
    substitution                  m_subst;
    std::unique_ptr<type_checker> m_tc;

    bool                          m_dep_elim;
    unsigned                      m_nparams;
    unsigned                      m_nindices;
    unsigned                      m_nminors;
    declaration                   m_I_decl;
    declaration                   m_cases_on_decl;

    void init_inductive_info(name const & n) {
        m_dep_elim      = inductive::has_dep_elim(m_env, n);
        m_nindices      = *inductive::get_num_indices(m_env, n);
        m_nparams       = *inductive::get_num_params(m_env, n);
        m_nminors       = *inductive::get_num_minor_premises(m_env, n);
        m_I_decl        = m_env.get(n);
        m_cases_on_decl = m_env.get({n, "cases_on"});
    }

    bool is_inversion_applicable(expr const & t) {
        buffer<expr> args;
        expr const & fn = get_app_args(t, args);
        if (!is_constant(fn))
            return false;
        if (!inductive::is_inductive_decl(m_env, const_name(fn)))
            return false;
        if (!m_env.find(name{const_name(fn), "cases_on"}) ||
            !m_env.find(name("eq")) || !m_env.find(name("heq")))
            return false;
        init_inductive_info(const_name(fn));
        if (args.size() != m_nindices + m_nparams)
            return false;
        return true;
    }

    pair<expr, expr> mk_eq(expr const & lhs, expr const & rhs) {
        expr lhs_type = m_tc->infer(lhs).first;
        expr rhs_type = m_tc->infer(rhs).first;
        level l       = sort_level(m_tc->ensure_type(lhs_type).first);
        constraint_seq cs;
        if (m_tc->is_def_eq(lhs_type, rhs_type, justification(), cs) && !cs) {
            return mk_pair(mk_app(mk_constant("eq", to_list(l)), lhs_type, lhs, rhs),
                           mk_app(mk_constant({"eq", "refl"}, to_list(l)), rhs_type, rhs));
        } else {
            return mk_pair(mk_app(mk_constant("heq", to_list(l)), lhs_type, lhs, rhs_type, rhs),
                           mk_app(mk_constant({"heq", "refl"}, to_list(l)), rhs_type, rhs));
        }
    }

    goal generalize_indices(goal const & g, expr const & h, expr const & h_type) {
        buffer<expr> hyps;
        g.get_hyps(hyps);
        expr m            = g.get_meta();
        expr m_type       = g.get_type();
        name h_new_name   = g.get_unused_name(local_pp_name(h));
        buffer<expr> I_args;
        expr const & I    = get_app_args(h_type, I_args);
        expr h_new_type  = mk_app(I, I_args.size() - m_nindices, I_args.data());
        expr d           = m_tc->whnf(m_tc->infer(h_new_type).first).first;
        unsigned eq_idx  = 1;
        name eq_prefix("H");
        buffer<expr> ts;
        buffer<expr> eqs;
        buffer<expr> refls;
        name t_prefix("t");
        unsigned nidx = 1;
        auto add_eq = [&](expr const & lhs, expr const & rhs) {
            pair<expr, expr> p = mk_eq(lhs, rhs);
            expr new_eq   = p.first;
            expr new_refl = p.second;
            eqs.push_back(mk_local(m_ngen.next(), g.get_unused_name(eq_prefix, eq_idx), new_eq, binder_info()));
            refls.push_back(new_refl);
        };
        for (unsigned i = I_args.size() - m_nindices; i < I_args.size(); i++) {
            expr t_type = binding_domain(d);
            expr t      = mk_local(m_ngen.next(), g.get_unused_name(t_prefix, nidx), t_type, binder_info());
            expr const & index = I_args[i];
            add_eq(t, index);
            h_new_type  = mk_app(h_new_type, t);
            hyps.push_back(t);
            ts.push_back(t);
            d           = instantiate(binding_body(d), t);
        }
        expr h_new    = mk_local(m_ngen.next(), h_new_name, h_new_type, local_info(h));
        if (m_dep_elim)
            add_eq(h_new, h);
        hyps.push_back(h_new);
        expr new_type = Pi(eqs, g.get_type());
        expr new_meta = mk_app(mk_metavar(m_ngen.next(), Pi(hyps, new_type)), hyps);
        goal new_g(new_meta, new_type);
        expr val      = g.abstract(mk_app(mk_app(mk_app(Fun(ts, Fun(h_new, new_meta)), m_nindices, I_args.end() - m_nindices), h),
                                          refls));
        m_subst.assign(g.get_name(), val);
        return new_g;
    }

    list<goal> apply_cases_on(goal const & g) {
        buffer<expr> hyps;
        g.get_hyps(hyps);
        expr const & h      = hyps.back();
        expr const & h_type = mlocal_type(h);
        buffer<expr> I_args;
        expr const & I      = get_app_args(h_type, I_args);
        expr g_type         = g.get_type();
        expr cases_on;
        if (length(m_cases_on_decl.get_univ_params()) != length(m_I_decl.get_univ_params())) {
            level g_lvl  = sort_level(m_tc->ensure_type(g_type).first);
            cases_on     = mk_constant({const_name(I), "cases_on"}, cons(g_lvl, const_levels(I)));
        } else {
            cases_on     = mk_constant({const_name(I), "cases_on"}, const_levels(I));
        }
        // add params
        cases_on          = mk_app(cases_on, m_nparams, I_args.data());
        // add type former
        expr type_former  = g_type;
        if (m_dep_elim)
            type_former   = Fun(h, type_former);
        type_former       = Fun(m_nindices, I_args.end() - m_nindices, type_former);
        cases_on          = mk_app(cases_on, type_former);
        // add indices
        cases_on          = mk_app(cases_on, m_nindices, I_args.end() - m_nindices);
        // add h
        cases_on          = mk_app(cases_on, h);
        buffer<expr> new_hyps;
        new_hyps.append(hyps.size() - m_nindices - 1, hyps.data());
        // add a subgoal for each minor premise of cases_on
        expr cases_on_type = m_tc->whnf(m_tc->infer(cases_on).first).first;
        buffer<goal> new_goals;
        for (unsigned i = 0; i < m_nminors; i++) {
            expr new_type = binding_domain(cases_on_type);
            expr new_meta = mk_app(mk_metavar(m_ngen.next(), Pi(new_hyps, new_type)), new_hyps);
            goal new_g(new_meta, new_type);
            new_goals.push_back(new_g);
            cases_on      = mk_app(cases_on, new_meta);
            cases_on_type = m_tc->whnf(binding_body(cases_on_type)).first; // the minor premises do not depend on each other
        }
        expr val        = g.abstract(cases_on);
        m_subst.assign(g.get_name(), val);
        return to_list(new_goals.begin(), new_goals.end());
    }

    // Store in \c r the number of arguments for each cases_on minor.
    void get_minors_nargs(buffer<unsigned> & r) {
        expr cases_on_type = m_cases_on_decl.get_type();
        for (unsigned i = 0; i < m_nparams + 1 + m_nindices + 1; i++)
            cases_on_type = binding_body(cases_on_type);
        for (unsigned i = 0; i < m_nminors; i++) {
            expr minor_type = binding_domain(cases_on_type);
            unsigned nargs  = 0;
            while (is_pi(minor_type)) {
                nargs++;
                minor_type = binding_body(minor_type);
            }
            r.push_back(nargs);
            cases_on_type   = binding_body(cases_on_type);
        }
    }

    list<goal> intros_minors_args(list<goal> gs) {
        buffer<unsigned> minors_nargs;
        get_minors_nargs(minors_nargs);
        lean_assert(length(gs) == minors_nargs.size());
        buffer<goal> new_gs;
        for (unsigned i = 0; i < minors_nargs.size(); i++) {
            goal const & g = head(gs);
            unsigned nargs = minors_nargs[i];
            buffer<expr> hyps;
            g.get_hyps(hyps);
            buffer<expr> new_hyps;
            new_hyps.append(hyps);
            expr g_type    = g.get_type();
            for (unsigned i = 0; i < nargs; i++) {
                expr type  = binding_domain(g_type);
                expr new_h = mk_local(m_ngen.next(), get_unused_name(binding_name(g_type), hyps), type, binder_info());
                new_hyps.push_back(new_h);
                g_type     = instantiate(binding_body(g_type), new_h);
            }
            g_type = head_beta_reduce(g_type);
            expr new_meta = mk_app(mk_metavar(m_ngen.next(), Pi(new_hyps, g_type)), new_hyps);
            goal new_g(new_meta, g_type);
            new_gs.push_back(new_g);
            expr val      = g.abstract(Fun(nargs, new_hyps.end() - nargs, new_meta));
            m_subst.assign(g.get_name(), val);
            gs = tail(gs);
        }
        return to_list(new_gs.begin(), new_gs.end());
    }

public:
    inversion_tac(environment const & env, io_state const & ios, proof_state const & ps):
        m_env(env), m_ios(ios), m_ps(ps),
        m_ngen(m_ps.get_ngen()),
        m_tc(mk_type_checker(m_env, m_ngen.mk_child(), m_ps.relax_main_opaque())) {
    }

    optional<proof_state> execute(name const & n) {
        goals const & gs  = m_ps.get_goals();
        if (empty(gs))
            return none_proof_state();
        goal  g           = head(gs);
        goals tail_gs     = tail(gs);
        auto p = g.find_hyp(n);
        if (!p)
            return none_proof_state();
        expr const & h    = p->first;
        expr h_type       = m_tc->whnf(mlocal_type(h)).first;
        if (!is_inversion_applicable(h_type))
            return none_proof_state();
        goal g1           = generalize_indices(g, h, h_type);
        list<goal> gs2    = apply_cases_on(g1);
        list<goal> gs3    = intros_minors_args(gs2);
        proof_state new_s(m_ps, append(gs3, tail_gs), m_subst, m_ngen);
        return some_proof_state(new_s);
    }
};

tactic inversion_tactic(name const & n) {
    auto fn = [=](environment const & env, io_state const & ios, proof_state const & ps) -> optional<proof_state> {
        inversion_tac tac(env, ios, ps);
        return tac.execute(n);
    };
    return tactic01(fn);
}

void initialize_inversion_tactic() {
    register_tac(name({"tactic", "inversion"}),
                 [](type_checker &, elaborate_fn const &, expr const & e, pos_info_provider const *) {
                     name n = tactic_expr_to_id(app_arg(e), "invalid 'inversion' tactic, argument must be an identifier");
                     return inversion_tactic(n);
                 });
}
void finalize_inversion_tactic() {}
}
<|endoftext|>


<commit_before>/*
    RawSpeed - RAW file decoder.

    Copyright (C) 2009-2014 Klaus Post
    Copyright (C) 2014-2015 Pedro Côrte-Real

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/

#include "decoders/MosDecoder.h"
#include "common/Common.h"                          // for uint32, uchar8
#include "common/Point.h"                           // for iPoint2D
#include "decoders/RawDecoder.h"                    // for RawDecoder
#include "decoders/RawDecoderException.h"           // for ThrowRDE
#include "decompressors/UncompressedDecompressor.h" // for UncompressedDeco...
#include "io/BitPumpMSB32.h"                        // for BitPumpMSB32
#include "io/ByteStream.h"                          // for ByteStream
#include "io/Endianness.h"                          // for getU32LE, getLE
#include "tiff/TiffEntry.h"                         // for TiffEntry
#include "tiff/TiffIFD.h"                           // for TiffRootIFD, Tif...
#include "tiff/TiffTag.h"                           // for TiffTag::TILEOFF...
#include <algorithm>                                // for move
#include <cstdio>                                   // for sscanf
#include <cstring>                                  // for memchr
#include <memory>                                   // for unique_ptr
#include <string>                                   // for string, allocator

using namespace std;

namespace RawSpeed {

class CameraMetaData;

MosDecoder::MosDecoder(TiffRootIFDOwner&& rootIFD, FileMap* file)
  : AbstractTiffDecoder(move(rootIFD), file)
{
  black_level = 0;

  if (mRootIFD->getEntryRecursive(MAKE)) {
    auto id = mRootIFD->getID();
    make = id.make;
    model = id.model;
  } else {
    TiffEntry *xmp = mRootIFD->getEntryRecursive(XMP);
    if (!xmp)
      ThrowRDE("MOS Decoder: Couldn't find the XMP");
    string xmpText = xmp->getString();
    make = getXMPTag(xmpText, "Make");
    model = getXMPTag(xmpText, "Model");
  }
}

string MosDecoder::getXMPTag(const string &xmp, const string &tag) {
  string::size_type start = xmp.find("<tiff:"+tag+">");
  string::size_type end = xmp.find("</tiff:"+tag+">");
  if (start == string::npos || end == string::npos || end <= start)
    ThrowRDE("MOS Decoder: Couldn't find tag '%s' in the XMP", tag.c_str());
  int startlen = tag.size()+7;
  return xmp.substr(start+startlen, end-start-startlen);
}

RawImage MosDecoder::decodeRawInternal() {
  uint32 off = 0;

  uint32 base = 8;
  // We get a pointer up to the end of the file as we check offset bounds later
  const uchar8 *insideTiff = mFile->getData(base, mFile->getSize()-base);
  if (getU32LE(insideTiff) == 0x49494949) {
    uint32 offset = getU32LE(insideTiff + 8);
    if (offset+base+4 > mFile->getSize())
      ThrowRDE("MOS: PhaseOneC offset out of bounds");

    uint32 entries = getU32LE(insideTiff + offset);
    uint32 pos = 8; // Skip another 4 bytes

    uint32 width=0, height=0, strip_offset=0, data_offset=0, wb_offset=0;
    while (entries--) {
      if (offset+base+pos+16 > mFile->getSize())
        ThrowRDE("MOS: PhaseOneC offset out of bounds");

      uint32 tag = getU32LE(insideTiff + offset + pos + 0);
      // uint32 type = getU32LE(insideTiff + offset + pos + 4);
      // uint32 len  = getU32LE(insideTiff + offset + pos + 8);
      uint32 data = getU32LE(insideTiff + offset + pos + 12);
      pos += 16;
      switch(tag) {
      case 0x107: wb_offset    = data+base;      break;
      case 0x108: width        = data;      break;
      case 0x109: height       = data;      break;
      case 0x10f: data_offset  = data+base; break;
      case 0x21c: strip_offset = data+base; break;
      case 0x21d: black_level  = data>>2;   break;
      }
    }
    if (width <= 0 || height <= 0)
      ThrowRDE("MOS: PhaseOneC couldn't find width and height");
    if (strip_offset+height*4 > mFile->getSize())
      ThrowRDE("MOS: PhaseOneC strip offsets out of bounds");
    if (data_offset > mFile->getSize())
      ThrowRDE("MOS: PhaseOneC data offset out of bounds");

    mRaw->dim = iPoint2D(width, height);
    mRaw->createData();

    DecodePhaseOneC(data_offset, strip_offset, width, height);

    const uchar8 *data = mFile->getData(wb_offset, 12);
    for(int i=0; i<3; i++) {
      mRaw->metadata.wbCoeffs[i] = getLE<float>(data + i * 4);
    }

    return mRaw;
  }

  const TiffIFD *raw = nullptr;

  if (mRootIFD->hasEntryRecursive(TILEOFFSETS)) {
    raw = mRootIFD->getIFDWithTag(TILEOFFSETS);
    off = raw->getEntry(TILEOFFSETS)->getU32();
  } else {
    raw = mRootIFD->getIFDWithTag(CFAPATTERN);
    off = raw->getEntry(STRIPOFFSETS)->getU32();
  }

  uint32 width = raw->getEntry(IMAGEWIDTH)->getU32();
  uint32 height = raw->getEntry(IMAGELENGTH)->getU32();
  mRaw->dim = iPoint2D(width, height);
  mRaw->createData();

  UncompressedDecompressor u(*mFile, off, mRaw, uncorrectedRawValues);

  int compression = raw->getEntry(COMPRESSION)->getU32();
  if (1 == compression) {
    if (getTiffEndianness(mFile) == big)
      u.decode16BitRawBEunpacked(width, height);
    else
      u.decode16BitRawUnpacked(width, height);
  }
  else if (99 == compression || 7 == compression) {
    ThrowRDE("MOS Decoder: Leaf LJpeg not yet supported");
    //LJpegPlain l(mFile, mRaw);
    //l.startDecoder(off, mFile->getSize()-off, 0, 0);
  } else
    ThrowRDE("MOS Decoder: Unsupported compression: %d", compression);

  return mRaw;
}

void MosDecoder::DecodePhaseOneC(uint32 data_offset, uint32 strip_offset, uint32 width, uint32 height)
{
  const int length[] = { 8,7,6,9,11,10,5,12,14,13 };

  for (uint32 row = 0; row < height; row++) {
    uint32 off =
        data_offset + getU32LE(mFile->getData(strip_offset + row * 4, 4));

    BitPumpMSB32 pump(mFile, off);
    uint32 pred[2], len[2];
    pred[0] = pred[1] = 0;
    auto *img = (ushort16 *)mRaw->getData(0, row);
    for (uint32 col=0; col < width; col++) {
      if (col >= (width & -8))
        len[0] = len[1] = 14;
      else if ((col & 7) == 0) {
        for (unsigned int &i : len) {
          uint32 j = 0;
          for (; j < 5 && !pump.getBitsSafe(1); j++);
          if (j--)
            i = length[j * 2 + pump.getBitsSafe(1)];
        }
      }

      int i = len[col & 1];
      if (i == 14)
        img[col] = pred[col & 1] = pump.getBitsSafe(16);
      else
        img[col] = pred[col & 1] += pump.getBitsSafe(i) + 1 - (1 << (i - 1));
    }
  }
}

void MosDecoder::checkSupportInternal(CameraMetaData *meta) {
  RawDecoder::checkCameraSupported(meta, make, model, "");
}

void MosDecoder::decodeMetaDataInternal(CameraMetaData *meta) {
  RawDecoder::setMetaData(meta, make, model, "", 0);

  // Fetch the white balance (see dcraw.c parse_mos for more metadata that can be gotten)
  if (mRootIFD->hasEntryRecursive(LEAFMETADATA)) {
    ByteStream bs = mRootIFD->getEntryRecursive(LEAFMETADATA)->getData();

    // We need at least a couple of bytes:
    // "NeutObj_neutrals" + 28 bytes binay + 4x uint as strings + 3x space + \0
    const uint32 minSize = 16+28+4+3+1;

    // dcraw does actual parsing, since we just want one field we bruteforce it
    while (bs.getRemainSize() > minSize) {
      if (bs.skipPrefix("NeutObj_neutrals", 16)) {
        bs.skipBytes(28);
        // check for nulltermination of string inside bounds
        if (!memchr(bs.peekData(bs.getRemainSize()), 0, bs.getRemainSize()))
          break;
        uint32 tmp[4] = {0};
        sscanf(bs.peekString(), "%u %u %u %u", &tmp[0], &tmp[1], &tmp[2], &tmp[3]);
        if (tmp[0] > 0 && tmp[1] > 0 && tmp[2] > 0 && tmp[3] > 0) {
          mRaw->metadata.wbCoeffs[0] = (float) tmp[0]/tmp[1];
          mRaw->metadata.wbCoeffs[1] = (float) tmp[0]/tmp[2];
          mRaw->metadata.wbCoeffs[2] = (float) tmp[0]/tmp[3];
        }
        break;
      }
      bs.skipBytes(1);
    }
  }

  if (black_level)
    mRaw->blackLevel = black_level;
}

} // namespace RawSpeed
<commit_msg>MosDecoder::decodeRawInternal(): UB: decrement properly<commit_after>/*
    RawSpeed - RAW file decoder.

    Copyright (C) 2009-2014 Klaus Post
    Copyright (C) 2014-2015 Pedro Côrte-Real

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/

#include "decoders/MosDecoder.h"
#include "common/Common.h"                          // for uint32, uchar8
#include "common/Point.h"                           // for iPoint2D
#include "decoders/RawDecoder.h"                    // for RawDecoder
#include "decoders/RawDecoderException.h"           // for ThrowRDE
#include "decompressors/UncompressedDecompressor.h" // for UncompressedDeco...
#include "io/BitPumpMSB32.h"                        // for BitPumpMSB32
#include "io/ByteStream.h"                          // for ByteStream
#include "io/Endianness.h"                          // for getU32LE, getLE
#include "tiff/TiffEntry.h"                         // for TiffEntry
#include "tiff/TiffIFD.h"                           // for TiffRootIFD, Tif...
#include "tiff/TiffTag.h"                           // for TiffTag::TILEOFF...
#include <algorithm>                                // for move
#include <cstdio>                                   // for sscanf
#include <cstring>                                  // for memchr
#include <memory>                                   // for unique_ptr
#include <string>                                   // for string, allocator

using namespace std;

namespace RawSpeed {

class CameraMetaData;

MosDecoder::MosDecoder(TiffRootIFDOwner&& rootIFD, FileMap* file)
  : AbstractTiffDecoder(move(rootIFD), file)
{
  black_level = 0;

  if (mRootIFD->getEntryRecursive(MAKE)) {
    auto id = mRootIFD->getID();
    make = id.make;
    model = id.model;
  } else {
    TiffEntry *xmp = mRootIFD->getEntryRecursive(XMP);
    if (!xmp)
      ThrowRDE("MOS Decoder: Couldn't find the XMP");
    string xmpText = xmp->getString();
    make = getXMPTag(xmpText, "Make");
    model = getXMPTag(xmpText, "Model");
  }
}

string MosDecoder::getXMPTag(const string &xmp, const string &tag) {
  string::size_type start = xmp.find("<tiff:"+tag+">");
  string::size_type end = xmp.find("</tiff:"+tag+">");
  if (start == string::npos || end == string::npos || end <= start)
    ThrowRDE("MOS Decoder: Couldn't find tag '%s' in the XMP", tag.c_str());
  int startlen = tag.size()+7;
  return xmp.substr(start+startlen, end-start-startlen);
}

RawImage MosDecoder::decodeRawInternal() {
  uint32 off = 0;

  uint32 base = 8;
  // We get a pointer up to the end of the file as we check offset bounds later
  const uchar8 *insideTiff = mFile->getData(base, mFile->getSize()-base);
  if (getU32LE(insideTiff) == 0x49494949) {
    uint32 offset = getU32LE(insideTiff + 8);
    if (offset+base+4 > mFile->getSize())
      ThrowRDE("MOS: PhaseOneC offset out of bounds");

    uint32 entries = getU32LE(insideTiff + offset);
    uint32 pos = 8; // Skip another 4 bytes

    uint32 width=0, height=0, strip_offset=0, data_offset=0, wb_offset=0;
    for (; entries > 0; entries--) {
      if (offset+base+pos+16 > mFile->getSize())
        ThrowRDE("MOS: PhaseOneC offset out of bounds");

      uint32 tag = getU32LE(insideTiff + offset + pos + 0);
      // uint32 type = getU32LE(insideTiff + offset + pos + 4);
      // uint32 len  = getU32LE(insideTiff + offset + pos + 8);
      uint32 data = getU32LE(insideTiff + offset + pos + 12);
      pos += 16;
      switch(tag) {
      case 0x107: wb_offset    = data+base;      break;
      case 0x108: width        = data;      break;
      case 0x109: height       = data;      break;
      case 0x10f: data_offset  = data+base; break;
      case 0x21c: strip_offset = data+base; break;
      case 0x21d: black_level  = data>>2;   break;
      }
    }
    if (width <= 0 || height <= 0)
      ThrowRDE("MOS: PhaseOneC couldn't find width and height");
    if (strip_offset+height*4 > mFile->getSize())
      ThrowRDE("MOS: PhaseOneC strip offsets out of bounds");
    if (data_offset > mFile->getSize())
      ThrowRDE("MOS: PhaseOneC data offset out of bounds");

    mRaw->dim = iPoint2D(width, height);
    mRaw->createData();

    DecodePhaseOneC(data_offset, strip_offset, width, height);

    const uchar8 *data = mFile->getData(wb_offset, 12);
    for(int i=0; i<3; i++) {
      mRaw->metadata.wbCoeffs[i] = getLE<float>(data + i * 4);
    }

    return mRaw;
  }

  const TiffIFD *raw = nullptr;

  if (mRootIFD->hasEntryRecursive(TILEOFFSETS)) {
    raw = mRootIFD->getIFDWithTag(TILEOFFSETS);
    off = raw->getEntry(TILEOFFSETS)->getU32();
  } else {
    raw = mRootIFD->getIFDWithTag(CFAPATTERN);
    off = raw->getEntry(STRIPOFFSETS)->getU32();
  }

  uint32 width = raw->getEntry(IMAGEWIDTH)->getU32();
  uint32 height = raw->getEntry(IMAGELENGTH)->getU32();
  mRaw->dim = iPoint2D(width, height);
  mRaw->createData();

  UncompressedDecompressor u(*mFile, off, mRaw, uncorrectedRawValues);

  int compression = raw->getEntry(COMPRESSION)->getU32();
  if (1 == compression) {
    if (getTiffEndianness(mFile) == big)
      u.decode16BitRawBEunpacked(width, height);
    else
      u.decode16BitRawUnpacked(width, height);
  }
  else if (99 == compression || 7 == compression) {
    ThrowRDE("MOS Decoder: Leaf LJpeg not yet supported");
    //LJpegPlain l(mFile, mRaw);
    //l.startDecoder(off, mFile->getSize()-off, 0, 0);
  } else
    ThrowRDE("MOS Decoder: Unsupported compression: %d", compression);

  return mRaw;
}

void MosDecoder::DecodePhaseOneC(uint32 data_offset, uint32 strip_offset, uint32 width, uint32 height)
{
  const int length[] = { 8,7,6,9,11,10,5,12,14,13 };

  for (uint32 row = 0; row < height; row++) {
    uint32 off =
        data_offset + getU32LE(mFile->getData(strip_offset + row * 4, 4));

    BitPumpMSB32 pump(mFile, off);
    uint32 pred[2], len[2];
    pred[0] = pred[1] = 0;
    auto *img = (ushort16 *)mRaw->getData(0, row);
    for (uint32 col=0; col < width; col++) {
      if (col >= (width & -8))
        len[0] = len[1] = 14;
      else if ((col & 7) == 0) {
        for (unsigned int &i : len) {
          uint32 j = 0;
          for (; j < 5 && !pump.getBitsSafe(1); j++);
          if (j--)
            i = length[j * 2 + pump.getBitsSafe(1)];
        }
      }

      int i = len[col & 1];
      if (i == 14)
        img[col] = pred[col & 1] = pump.getBitsSafe(16);
      else
        img[col] = pred[col & 1] += pump.getBitsSafe(i) + 1 - (1 << (i - 1));
    }
  }
}

void MosDecoder::checkSupportInternal(CameraMetaData *meta) {
  RawDecoder::checkCameraSupported(meta, make, model, "");
}

void MosDecoder::decodeMetaDataInternal(CameraMetaData *meta) {
  RawDecoder::setMetaData(meta, make, model, "", 0);

  // Fetch the white balance (see dcraw.c parse_mos for more metadata that can be gotten)
  if (mRootIFD->hasEntryRecursive(LEAFMETADATA)) {
    ByteStream bs = mRootIFD->getEntryRecursive(LEAFMETADATA)->getData();

    // We need at least a couple of bytes:
    // "NeutObj_neutrals" + 28 bytes binay + 4x uint as strings + 3x space + \0
    const uint32 minSize = 16+28+4+3+1;

    // dcraw does actual parsing, since we just want one field we bruteforce it
    while (bs.getRemainSize() > minSize) {
      if (bs.skipPrefix("NeutObj_neutrals", 16)) {
        bs.skipBytes(28);
        // check for nulltermination of string inside bounds
        if (!memchr(bs.peekData(bs.getRemainSize()), 0, bs.getRemainSize()))
          break;
        uint32 tmp[4] = {0};
        sscanf(bs.peekString(), "%u %u %u %u", &tmp[0], &tmp[1], &tmp[2], &tmp[3]);
        if (tmp[0] > 0 && tmp[1] > 0 && tmp[2] > 0 && tmp[3] > 0) {
          mRaw->metadata.wbCoeffs[0] = (float) tmp[0]/tmp[1];
          mRaw->metadata.wbCoeffs[1] = (float) tmp[0]/tmp[2];
          mRaw->metadata.wbCoeffs[2] = (float) tmp[0]/tmp[3];
        }
        break;
      }
      bs.skipBytes(1);
    }
  }

  if (black_level)
    mRaw->blackLevel = black_level;
}

} // namespace RawSpeed
<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////////////////////////
///
/// \file hardware_concurrency.hpp
/// ------------------------------
///
/// (c) Copyright Domagoj Saric 2016 - 2017.
///
///  Use, modification and distribution are subject to the
///  Boost Software License, Version 1.0. (See accompanying file
///  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
///
///  See http://www.boost.org for most recent version.
///
////////////////////////////////////////////////////////////////////////////////
//------------------------------------------------------------------------------
#ifndef hardware_concurrency_hpp__070F2563_7F3C_40D5_85C9_1289E8DEEDC8
#define hardware_concurrency_hpp__070F2563_7F3C_40D5_85C9_1289E8DEEDC8
#pragma once
//------------------------------------------------------------------------------
#ifndef BOOST_SWEATER_MAX_HARDWARE_CONCURRENCY
#   if defined( __ANDROID__ )
#	    define BOOST_SWEATER_MAX_HARDWARE_CONCURRENCY 8 // SGS6 has 8 cores
#   elif defined( __APPLE__ )
#       if defined( __aarch64__ )
#           define BOOST_SWEATER_MAX_HARDWARE_CONCURRENCY 3 // iPad 2 Air
#       elif defined( __arm__ )
#           define BOOST_SWEATER_MAX_HARDWARE_CONCURRENCY 2
#       else
#          define BOOST_SWEATER_MAX_HARDWARE_CONCURRENCY 0 // desktop or simulator
#       endif // arch
#   elif defined(__WINDOWS_PHONE__)
#	    define BOOST_SWEATER_MAX_HARDWARE_CONCURRENCY 2
#   else
#	    define BOOST_SWEATER_MAX_HARDWARE_CONCURRENCY 0
#   endif // platform
#endif // BOOST_SWEATER_MAX_HARDWARE_CONCURRENCY

#ifdef _MSC_VER
#   include <yvals.h>
#   pragma detect_mismatch( "BOOST_SWEATER_MAX_HARDWARE_CONCURRENCY", _STRINGIZE( BOOST_SWEATER_MAX_HARDWARE_CONCURRENCY ) )
#endif // _MSC_VER

#include <boost/config_ex.hpp>

#include <cstdint>
#include <thread>
//------------------------------------------------------------------------------
namespace boost
{
//------------------------------------------------------------------------------
namespace sweater
{
//------------------------------------------------------------------------------

#if defined( __arm__ ) || defined( __aarch64__ ) || defined( __ANDROID__ ) || defined( __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ )
using hardware_concurrency_t = std::uint_fast8_t;
#else
using hardware_concurrency_t = std::uint_fast16_t; // e.g. Intel MIC
#endif

#ifdef BOOST_MSVC // no inline variables even in VS 15.3
BOOST_OVERRIDABLE_SYMBOL
#else
inline
#endif // BOOST_MSVC
auto const hardware_concurrency( static_cast<hardware_concurrency_t>( std::thread::hardware_concurrency() ) );

//------------------------------------------------------------------------------
} // namespace sweater
//------------------------------------------------------------------------------
} // namespace boost
//------------------------------------------------------------------------------
#endif // hardware_concurrency_hpp
<commit_msg>Android, Apple: fixed/updated BOOST_SWEATER_MAX_HARDWARE_CONCURRENCY definition to support latest devices.<commit_after>////////////////////////////////////////////////////////////////////////////////
///
/// \file hardware_concurrency.hpp
/// ------------------------------
///
/// (c) Copyright Domagoj Saric 2016 - 2017.
///
///  Use, modification and distribution are subject to the
///  Boost Software License, Version 1.0. (See accompanying file
///  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
///
///  See http://www.boost.org for most recent version.
///
////////////////////////////////////////////////////////////////////////////////
//------------------------------------------------------------------------------
#ifndef hardware_concurrency_hpp__070F2563_7F3C_40D5_85C9_1289E8DEEDC8
#define hardware_concurrency_hpp__070F2563_7F3C_40D5_85C9_1289E8DEEDC8
#pragma once
//------------------------------------------------------------------------------
#ifndef BOOST_SWEATER_MAX_HARDWARE_CONCURRENCY
#   if defined( __ANDROID__ )
#       if defined( __aarch64__ )
#           define BOOST_SWEATER_MAX_HARDWARE_CONCURRENCY 32 // SGS6 8, Meizu PRO 6 10 cores
#       elif defined( __arm__ )
#           define BOOST_SWEATER_MAX_HARDWARE_CONCURRENCY 8
#       endif // arch
#   elif defined( __APPLE__ )
#       if defined( __aarch64__ )
#           define BOOST_SWEATER_MAX_HARDWARE_CONCURRENCY 8 // iPad 2 Air 3, iPhone 8 6 cores
#       elif defined( __arm__ )
#           define BOOST_SWEATER_MAX_HARDWARE_CONCURRENCY 2
#       else
#          define BOOST_SWEATER_MAX_HARDWARE_CONCURRENCY 0 // desktop or simulator
#       endif // arch
#   elif defined(__WINDOWS_PHONE__)
#	    define BOOST_SWEATER_MAX_HARDWARE_CONCURRENCY 2
#   else
#	    define BOOST_SWEATER_MAX_HARDWARE_CONCURRENCY 0
#   endif // platform
#endif // BOOST_SWEATER_MAX_HARDWARE_CONCURRENCY

#ifdef _MSC_VER
#   include <yvals.h>
#   pragma detect_mismatch( "BOOST_SWEATER_MAX_HARDWARE_CONCURRENCY", _STRINGIZE( BOOST_SWEATER_MAX_HARDWARE_CONCURRENCY ) )
#endif // _MSC_VER

#include <boost/config_ex.hpp>

#include <cstdint>
#include <thread>
//------------------------------------------------------------------------------
namespace boost
{
//------------------------------------------------------------------------------
namespace sweater
{
//------------------------------------------------------------------------------

#if defined( __arm__ ) || defined( __aarch64__ ) || defined( __ANDROID__ ) || defined( __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ )
using hardware_concurrency_t = std::uint_fast8_t;
#else
using hardware_concurrency_t = std::uint_fast16_t; // e.g. Intel MIC
#endif

#ifdef BOOST_MSVC // no inline variables even in VS 15.3
BOOST_OVERRIDABLE_SYMBOL
#else
inline
#endif // BOOST_MSVC
auto const hardware_concurrency( static_cast<hardware_concurrency_t>( std::thread::hardware_concurrency() ) );

//------------------------------------------------------------------------------
} // namespace sweater
//------------------------------------------------------------------------------
} // namespace boost
//------------------------------------------------------------------------------
#endif // hardware_concurrency_hpp
<|endoftext|>


<commit_before>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2011 Artem Pavlenko
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *****************************************************************************/

//$Id$

#ifndef MAPNIK_GEOMETRY_WKT_GENERATOR_HPP
#define MAPNIK_GEOMETRY_WKT_GENERATOR_HPP

// mapnik
#include <mapnik/global.hpp>
#include <mapnik/geometry.hpp>
#include <mapnik/util/vertex_iterator.hpp>
#include <mapnik/util/container_adapter.hpp>

// boost
#include <boost/tuple/tuple.hpp>
#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_fusion.hpp>
#include <boost/spirit/include/phoenix_function.hpp>
#include <boost/spirit/home/phoenix/statement/if.hpp>
#include <boost/fusion/include/boost_tuple.hpp>

//#define BOOST_SPIRIT_USE_PHOENIX_V3 1

namespace mapnik { namespace util {

namespace karma = boost::spirit::karma;
namespace phoenix = boost::phoenix;

struct get_type
{
    template <typename T>
    struct result { typedef int type; };

    int operator() (geometry_type const& geom) const
    {
        return (int)geom.type();
    }
};

struct get_first
{
    template <typename T>
    struct result { typedef geometry_type::value_type const type; };

    geometry_type::value_type const operator() (geometry_type const& geom) const
    {
        geometry_type::value_type coord;
        boost::get<0>(coord) = geom.get_vertex(0,&boost::get<1>(coord),&boost::get<2>(coord));
        return coord;
    }
};

template <typename T>
struct coordinate_policy : karma::real_policies<T>
{
    typedef boost::spirit::karma::real_policies<T> base_type;
    static int floatfield(T n) { return base_type::fmtflags::fixed; }
};

template <typename OutputIterator>
struct wkt_generator :
        karma::grammar<OutputIterator, geometry_type const& ()>
{

    wkt_generator()
        : wkt_generator::base_type(wkt)
    {
        using boost::spirit::karma::uint_;
        using boost::spirit::karma::_val;
        using boost::spirit::karma::_1;
        using boost::spirit::karma::lit;
        using boost::spirit::karma::_a;
        using boost::spirit::karma::_r1;
        using boost::spirit::karma::eps;

        wkt = point | linestring | polygon
            ;

        point = &uint_(mapnik::Point)[_1 = _type(_val)]
            << lit("Point(") << point_coord [_1 = _first(_val)] << lit(')')
            ;

        linestring = &uint_(mapnik::LineString)[_1 = _type(_val)]
            << lit("LineString(")
            << coords
            << lit(')')
            ;

        polygon = &uint_(mapnik::Polygon)[_1 = _type(_val)]
            << lit("Polygon(")
            << coords2
            << lit("))")
            ;

        point_coord = &uint_ << coord_type << lit(' ') << coord_type
            ;

        polygon_coord %= ( &uint_(mapnik::SEG_MOVETO) << eps[_r1 += 1]
                   << karma::string[ if_ (_r1 > 1) [_1 = "),("]
                                     .else_[_1 = "("] ] | &uint_ << ",")
            << coord_type
            << lit(' ')
            << coord_type
            ;

        coords2 %= *polygon_coord(_a)
            ;

        coords = point_coord % lit(',')
            ;

    }
    // rules
    karma::rule<OutputIterator, geometry_type const& ()> wkt;
    karma::rule<OutputIterator, geometry_type const& ()> point;
    karma::rule<OutputIterator, geometry_type const& ()> linestring;
    karma::rule<OutputIterator, geometry_type const& ()> polygon;

    karma::rule<OutputIterator, geometry_type const& ()> coords;
    karma::rule<OutputIterator, karma::locals<unsigned>, geometry_type const& ()> coords2;
    karma::rule<OutputIterator, geometry_type::value_type ()> point_coord;
    karma::rule<OutputIterator, geometry_type::value_type const& (unsigned& )> polygon_coord;

    // phoenix functions
    phoenix::function<get_type > _type;
    phoenix::function<get_first> _first;
    //
    karma::real_generator<double, coordinate_policy<double> > coord_type;

};

}}

#endif // MAPNIK_GEOMETRY_WKT_GENERATOR_HPP
<commit_msg>+ increase coordinates precision<commit_after>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2011 Artem Pavlenko
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *****************************************************************************/

//$Id$

#ifndef MAPNIK_GEOMETRY_WKT_GENERATOR_HPP
#define MAPNIK_GEOMETRY_WKT_GENERATOR_HPP

// mapnik
#include <mapnik/global.hpp>
#include <mapnik/geometry.hpp>
#include <mapnik/util/vertex_iterator.hpp>
#include <mapnik/util/container_adapter.hpp>

// boost
#include <boost/tuple/tuple.hpp>
#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_fusion.hpp>
#include <boost/spirit/include/phoenix_function.hpp>
#include <boost/spirit/home/phoenix/statement/if.hpp>
#include <boost/fusion/include/boost_tuple.hpp>

//#define BOOST_SPIRIT_USE_PHOENIX_V3 1

namespace mapnik { namespace util {

namespace karma = boost::spirit::karma;
namespace phoenix = boost::phoenix;

struct get_type
{
    template <typename T>
    struct result { typedef int type; };

    int operator() (geometry_type const& geom) const
    {
        return (int)geom.type();
    }
};

struct get_first
{
    template <typename T>
    struct result { typedef geometry_type::value_type const type; };

    geometry_type::value_type const operator() (geometry_type const& geom) const
    {
        geometry_type::value_type coord;
        boost::get<0>(coord) = geom.get_vertex(0,&boost::get<1>(coord),&boost::get<2>(coord));
        return coord;
    }
};

template <typename T>
struct coordinate_policy : karma::real_policies<T>
{
    typedef boost::spirit::karma::real_policies<T> base_type;
    static int floatfield(T n) { return base_type::fmtflags::fixed; }
    static unsigned precision(T n) { return 6u ;}
};

template <typename OutputIterator>
struct wkt_generator :
        karma::grammar<OutputIterator, geometry_type const& ()>
{

    wkt_generator()
        : wkt_generator::base_type(wkt)
    {
        using boost::spirit::karma::uint_;
        using boost::spirit::karma::_val;
        using boost::spirit::karma::_1;
        using boost::spirit::karma::lit;
        using boost::spirit::karma::_a;
        using boost::spirit::karma::_r1;
        using boost::spirit::karma::eps;

        wkt = point | linestring | polygon
            ;

        point = &uint_(mapnik::Point)[_1 = _type(_val)]
            << lit("Point(") << point_coord [_1 = _first(_val)] << lit(')')
            ;

        linestring = &uint_(mapnik::LineString)[_1 = _type(_val)]
            << lit("LineString(")
            << coords
            << lit(')')
            ;

        polygon = &uint_(mapnik::Polygon)[_1 = _type(_val)]
            << lit("Polygon(")
            << coords2
            << lit("))")
            ;

        point_coord = &uint_ << coord_type << lit(' ') << coord_type
            ;

        polygon_coord %= ( &uint_(mapnik::SEG_MOVETO) << eps[_r1 += 1]
                   << karma::string[ if_ (_r1 > 1) [_1 = "),("]
                                     .else_[_1 = "("] ] | &uint_ << ",")
            << coord_type
            << lit(' ')
            << coord_type
            ;

        coords2 %= *polygon_coord(_a)
            ;

        coords = point_coord % lit(',')
            ;

    }
    // rules
    karma::rule<OutputIterator, geometry_type const& ()> wkt;
    karma::rule<OutputIterator, geometry_type const& ()> point;
    karma::rule<OutputIterator, geometry_type const& ()> linestring;
    karma::rule<OutputIterator, geometry_type const& ()> polygon;

    karma::rule<OutputIterator, geometry_type const& ()> coords;
    karma::rule<OutputIterator, karma::locals<unsigned>, geometry_type const& ()> coords2;
    karma::rule<OutputIterator, geometry_type::value_type ()> point_coord;
    karma::rule<OutputIterator, geometry_type::value_type const& (unsigned& )> polygon_coord;

    // phoenix functions
    phoenix::function<get_type > _type;
    phoenix::function<get_first> _first;
    //
    karma::real_generator<double, coordinate_policy<double> > coord_type;

};

}}

#endif // MAPNIK_GEOMETRY_WKT_GENERATOR_HPP
<|endoftext|>


<commit_before>/***************************************************************
 *
 * Copyright (C) 1990-2020, Condor Team, Computer Sciences Department,
 * University of Wisconsin-Madison, WI.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you
 * may not use this file except in compliance with the License.  You may
 * obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 ***************************************************************/

#include "condor_common.h"
#include "condor_config.h"
#include "match_prefix.h"
#include <termios.h>
#include <unistd.h>
#include <grp.h>
#include <signal.h>
#include <algorithm>

// condor_nsenter
//
// This is a replacement for the linux nsenter command to launch a
// shell inside a container.  We need this when running
// condor_ssh_to_job to a job that has been launched inside singularity
// Docker jobs use docker exec to enter the container, but there is no
// equivalent in singularity.  Standard nsenter isn't sufficient, as it
// does not create a pty for the shell, and we need different command
// line arguments to enter a non-setuid singularity container.  This
// is harder because the starter can't tell if singularity is setuid easily.
//
// The architecture for ssh-to-job to a contained job is to land in an sshd
// the starter forks *outside* the container.  This is important because we
// never want to assume anything about the container, even that there is an sshd
// inside it.  After the sshd starts, a script runs which runs condor_docker_enter
// (even for singularity), which connects to a Unix Domain Socket, passes
// stdin/out/err to the starter, which passes those to condor_nsenter, which
// is runs as root.  Rootly privilege is required to enter a setuid namespace
// so this is how we acquire.
//
// condor_nsenter enters the namespace, taking care to try to enter the user
// namespace, which is only set up for non-setuid singularity, and drops
// privileges to the uid and gid passed on the command line.  It sets up
// a pty for the shell to use, so all interactive processses will work.
//
// A final problem is environment variables.  Singularity, especially when
// managing GPUs, sets some nvidia-related environment variables that the
// condor starter doesn't know about.  So, condor_nsenter extracts the environment
// variables from the contained job process, and sets them in the shell
// it spawns.

void
usage( char *cmd )
{
	fprintf(stderr,"Usage: %s [options] condor_* ....\n",cmd);
	fprintf(stderr,"Where options are:\n");
	fprintf(stderr,"-t target_pid:\n");
	fprintf(stderr,"-S user_id:\n");
	fprintf(stderr,"-G group_id:\n");
	fprintf(stderr,"    -help              Display options\n");
}

// Before we exit, we need to reset the pty back to normal
// if we put it in raw mode

bool pty_is_raw = false;
struct termios old_tio;
void reset_pty_and_exit(int signo) {
	if (pty_is_raw)
		tcsetattr(0, TCSAFLUSH, &old_tio);
	exit(signo);
}

int main( int argc, char *argv[] )
{
	std::string condor_prefix;
	pid_t pid = 0;
	uid_t uid = 0;
	gid_t gid = 0;

	// parse command line args
	for( int i=1; i<argc; i++ ) {
		if(is_arg_prefix(argv[i],"-help")) {
			usage(argv[0]);
			exit(1);
		}

		// target pid to enter
		if(is_arg_prefix(argv[i],"-t")) {
			pid = atoi(argv[i + 1]);
			i++;
		}

		// uid to switch to
		if(is_arg_prefix(argv[i],"-S")) {
			uid = atoi(argv[i + 1]);
			i++;
		}

		// gid to switch to
		if(is_arg_prefix(argv[i],"-G")) {
			gid = atoi(argv[i + 1]);
			i++;
		}
	}

	if (pid < 1) {
		fprintf(stderr, "missing -t argument > 1\n");
		exit(1);
	}

	if (uid == 0) {
		fprintf(stderr, "missing -S argument > 1\n");
		exit(1);
	}

	if (gid == 0) {
		fprintf(stderr, "missing -G argument > 1\n");
		exit(1);
	}

	// slurp the enviroment out of our victim
	std::string env;
	std::string envfile;
	formatstr(envfile, "/proc/%d/environ", pid);

	int e = open(envfile.c_str(), O_RDONLY);
	if (e < 0) {
		fprintf(stderr, "Can't open %s %s\n", envfile.c_str(), strerror(errno));
		exit(1);
	}
	char buf[512];
	int bytesRead;
	while ((bytesRead = read(e, &buf, 512)) > 0) {
		env.append(buf, bytesRead);
	}
	close(e);

	// If the environment contains a string with the value of the
	// path to the container's scratch directory outside the container
	// replace it with the path inside the container.

	std::string outsidePath;
	if (getenv("_CONDOR_SCRATCH_DIR_OUTSIDE_CONTAINER")) {
		outsidePath = getenv("_CONDOR_SCRATCH_DIR_OUTSIDE_CONTAINER");
	}
	std::string insidePath;
	if (getenv("_CONDOR_SCRATCH_DIR")) {
		insidePath = getenv("_CONDOR_SCRATCH_DIR");
	}
	if (outsidePath.length() > 0) {
		size_t pos = 0;
		do {
			pos = env.find(outsidePath, pos);
			if (pos != std::string::npos) {
				env.replace(pos, outsidePath.length(), insidePath);
				pos += insidePath.length();
			}
		} while (pos != std::string::npos);
	}
	// we probably replaced the value of _CONDOR_SCRATCH_DIR_OUTSIDE_CONTAINER
	// with the inside value, so set that back
	size_t pos = 0;
	pos = env.find("_CONDOR_SCRATCH_DIR_OUTSIDE_CONTAINER=", pos);
	if (pos != std::string::npos) {
		size_t eqpos = env.find("=", pos);
		eqpos++;
		env.replace(eqpos, insidePath.length(), outsidePath);
	}

	// make a vector to hold all the pointers to env entries
	std::vector<const char *> envp;

	// copy DISPLAY from outside to inside.  sshd has set this,
	// it is the only one from the outside we need on the inside,
	// and one way that an ssh-to-job shell is different than the job.
	std::string display;
	if (getenv("DISPLAY")) {
		formatstr(display, "DISPLAY=%s", getenv("DISPLAY"));
		envp.push_back(display.c_str());
	}

	// the first one
	envp.push_back(env.c_str());
	auto it = env.cbegin();
	while (env.cend() != (it = std::find(it, env.cend(), '\0'))) {
		// skip past null terminator
		it++;
		if (& (*it)  != nullptr) {
			envp.push_back(& (*it));
		}
	}
	envp.push_back(nullptr);
	envp.push_back(nullptr);

	std::string filename;

	// start changing namespaces.  Note that once we do this, things
	// get funny in this process
	formatstr(filename, "/proc/%d/ns/uts", pid);
	int fd = open(filename.c_str(), O_RDONLY);
	if (fd < 0) {
		fprintf(stderr, "Can't open uts namespace: %d %s\n", errno, strerror(errno));
		exit(1);
	}
	int r = setns(fd, 0);
	close(fd);
	if (r < 0) {
		// This means an unprivileged singularity, most likely
		// need to set user namespace instead.
		formatstr(filename, "/proc/%d/ns/user", pid);
		fd = open(filename.c_str(), O_RDONLY);
		if (fd < 0) {
			fprintf(stderr, "Can't open user namespace: %d %s\n", errno, strerror(errno));
			exit(1);
		}
		r = setns(fd, 0);
		close(fd);
		if (r < 0) {
			fprintf(stderr, "Can't setns to user namespace: %s\n", strerror(errno));
			exit(1);
		}
	}

	// now the pid namespace
	formatstr(filename, "/proc/%d/ns/pid", pid);
	fd = open(filename.c_str(), O_RDONLY);
	if (fd < 0) {
		fprintf(stderr, "Can't open pid namespace: %s\n", strerror(errno));
		exit(1);
	}
	r = setns(fd, 0);
	close(fd);
	if (r < 0) {
		fprintf(stderr, "Can't setns to pid namespace: %s\n", strerror(errno));
		exit(1);
	}

	// finally the mnt namespace
	formatstr(filename, "/proc/%d/ns/mnt", pid);
	fd = open(filename.c_str(), O_RDONLY);
	if (fd < 0) {
		fprintf(stderr, "Can't open mnt namespace: %s\n", strerror(errno));
		exit(1);
	}
	r = setns(fd, 0);
	close(fd);
	if (r < 0) {
		fprintf(stderr, "Can't setns to mnt namespace: %s\n", strerror(errno));
		exit(1);
	}

	setgroups(0, nullptr);

	// order matters!
	r = setgid(gid);
	if (r < 0) {
		fprintf(stderr, "Can't setgid to %d\n", gid);
		exit(1);
	}
	r = setuid(uid);
	if (r < 0) {
		fprintf(stderr, "Can't setuid to %d\n", uid);
		exit(1);
	}

	struct winsize win;
	ioctl(0, TIOCGWINSZ, &win);

	// now the pty handling
	int masterPty = -1;
	int workerPty = -1;
	masterPty = open("/dev/ptmx", O_RDWR);
	unlockpt(masterPty);

	if (masterPty < 0) {
		fprintf(stderr, "Can't open master pty %s\n", strerror(errno));
		exit(1);
	} else {
		workerPty = open(ptsname(masterPty), O_RDWR);
		if (workerPty < 0) {
			fprintf(stderr, "Can't open worker pty %s\n", strerror(errno));
			exit(1);
		}
	}
	int childpid = fork();
	if (childpid == 0) {

		// in the child --

		close(0);
		close(1);
		close(2);
		close(masterPty);

		dup2(workerPty, 0);
		dup2(workerPty, 1);
		dup2(workerPty, 2);

		if (getenv("_CONDOR_SCRATCH_DIR")) {
			int r = chdir(getenv("_CONDOR_SCRATCH_DIR"));
			if (r < 0) {
				printf("Cannot chdir to %s: %s\n", getenv("_CONDOR_SCRATCH_DIR"), strerror(errno));
			}
 		}

		// make this process group leader so shell job control works
		setsid();

		// Make the pty the controlling terminal
		ioctl(workerPty, TIOCSCTTY, 0);

		// and make it the process group leader
		tcsetpgrp(workerPty, getpid());

		// and set the window size properly
		ioctl(0, TIOCSWINSZ, &win);

		// Finally, launch the shell
		execle("/bin/sh", "/bin/sh", "-l", "-i", nullptr, envp.data());

		// Only get here if exec fails
		fprintf(stderr, "exec failed %d\n", errno);
		exit(errno);

	} else {

		// the parent
		fd_set readfds, writefds, exceptfds;
		bool keepGoing = true;

		// put the pty in raw mode
		struct termios tio;
		tcgetattr(0, &tio);
		pty_is_raw = true;
		old_tio = tio;
		tio.c_iflag &= ~(ICRNL);
		tio.c_oflag &= ~(OPOST);
		tio.c_cflag |= (CS8);
		tio.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
		tio.c_cc[VMIN] = 1;
		tio.c_cc[VTIME] = 0;
		tcsetattr(0, TCSAFLUSH, &tio);

		struct sigaction handler;
		struct sigaction oldhandler;
		handler.sa_handler = reset_pty_and_exit;
		handler.sa_flags   = 0;
		sigemptyset(&handler.sa_mask);

		sigaction(SIGCHLD, &handler, &oldhandler);

		while (keepGoing) {
			FD_ZERO(&readfds);
			FD_ZERO(&writefds);
			FD_ZERO(&exceptfds);

			FD_SET(0, &readfds);
			FD_SET(masterPty, &readfds);

			select(masterPty + 1, &readfds, &writefds, &exceptfds, nullptr);

			if (FD_ISSET(masterPty, &readfds)) {
				char buf;
				int r = read(masterPty, &buf, 1);
				if (r > 0) {
					int ret = write(1, &buf, 1);
					if (ret < 0) {
						reset_pty_and_exit(0);
					}
				} else {
					keepGoing = false;
				}
			}

			if (FD_ISSET(0, &readfds)) {
				char buf;
				int r = read(0, &buf, 1);
				if (r > 0) {
					int ret = write(masterPty, &buf, 1);
					if (ret < 0) {
						reset_pty_and_exit(0);
					}
				} else {
					keepGoing = false;
				}
			}
		}
		int status;
		waitpid(childpid, &status, 0);
	}
	return 0;
}

<commit_msg>nsenter: Use a buffer to forward data through TTY FDs.<commit_after>/***************************************************************
 *
 * Copyright (C) 1990-2020, Condor Team, Computer Sciences Department,
 * University of Wisconsin-Madison, WI.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you
 * may not use this file except in compliance with the License.  You may
 * obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 ***************************************************************/

#include "condor_common.h"
#include "condor_config.h"
#include "match_prefix.h"
#include <termios.h>
#include <unistd.h>
#include <grp.h>
#include <signal.h>
#include <algorithm>

// condor_nsenter
//
// This is a replacement for the linux nsenter command to launch a
// shell inside a container.  We need this when running
// condor_ssh_to_job to a job that has been launched inside singularity
// Docker jobs use docker exec to enter the container, but there is no
// equivalent in singularity.  Standard nsenter isn't sufficient, as it
// does not create a pty for the shell, and we need different command
// line arguments to enter a non-setuid singularity container.  This
// is harder because the starter can't tell if singularity is setuid easily.
//
// The architecture for ssh-to-job to a contained job is to land in an sshd
// the starter forks *outside* the container.  This is important because we
// never want to assume anything about the container, even that there is an sshd
// inside it.  After the sshd starts, a script runs which runs condor_docker_enter
// (even for singularity), which connects to a Unix Domain Socket, passes
// stdin/out/err to the starter, which passes those to condor_nsenter, which
// is runs as root.  Rootly privilege is required to enter a setuid namespace
// so this is how we acquire.
//
// condor_nsenter enters the namespace, taking care to try to enter the user
// namespace, which is only set up for non-setuid singularity, and drops
// privileges to the uid and gid passed on the command line.  It sets up
// a pty for the shell to use, so all interactive processses will work.
//
// A final problem is environment variables.  Singularity, especially when
// managing GPUs, sets some nvidia-related environment variables that the
// condor starter doesn't know about.  So, condor_nsenter extracts the environment
// variables from the contained job process, and sets them in the shell
// it spawns.

void
usage( char *cmd )
{
	fprintf(stderr,"Usage: %s [options] condor_* ....\n",cmd);
	fprintf(stderr,"Where options are:\n");
	fprintf(stderr,"-t target_pid:\n");
	fprintf(stderr,"-S user_id:\n");
	fprintf(stderr,"-G group_id:\n");
	fprintf(stderr,"    -help              Display options\n");
}

// Before we exit, we need to reset the pty back to normal
// if we put it in raw mode

bool pty_is_raw = false;
struct termios old_tio;
void reset_pty_and_exit(int signo) {
	if (pty_is_raw)
		tcsetattr(0, TCSAFLUSH, &old_tio);
	exit(signo);
}

int main( int argc, char *argv[] )
{
	std::string condor_prefix;
	pid_t pid = 0;
	uid_t uid = 0;
	gid_t gid = 0;

	// parse command line args
	for( int i=1; i<argc; i++ ) {
		if(is_arg_prefix(argv[i],"-help")) {
			usage(argv[0]);
			exit(1);
		}

		// target pid to enter
		if(is_arg_prefix(argv[i],"-t")) {
			pid = atoi(argv[i + 1]);
			i++;
		}

		// uid to switch to
		if(is_arg_prefix(argv[i],"-S")) {
			uid = atoi(argv[i + 1]);
			i++;
		}

		// gid to switch to
		if(is_arg_prefix(argv[i],"-G")) {
			gid = atoi(argv[i + 1]);
			i++;
		}
	}

	if (pid < 1) {
		fprintf(stderr, "missing -t argument > 1\n");
		exit(1);
	}

	if (uid == 0) {
		fprintf(stderr, "missing -S argument > 1\n");
		exit(1);
	}

	if (gid == 0) {
		fprintf(stderr, "missing -G argument > 1\n");
		exit(1);
	}

	// slurp the enviroment out of our victim
	std::string env;
	std::string envfile;
	formatstr(envfile, "/proc/%d/environ", pid);

	int e = open(envfile.c_str(), O_RDONLY);
	if (e < 0) {
		fprintf(stderr, "Can't open %s %s\n", envfile.c_str(), strerror(errno));
		exit(1);
	}
	char buf[512];
	int bytesRead;
	while ((bytesRead = read(e, &buf, 512)) > 0) {
		env.append(buf, bytesRead);
	}
	close(e);

	// If the environment contains a string with the value of the
	// path to the container's scratch directory outside the container
	// replace it with the path inside the container.

	std::string outsidePath;
	if (getenv("_CONDOR_SCRATCH_DIR_OUTSIDE_CONTAINER")) {
		outsidePath = getenv("_CONDOR_SCRATCH_DIR_OUTSIDE_CONTAINER");
	}
	std::string insidePath;
	if (getenv("_CONDOR_SCRATCH_DIR")) {
		insidePath = getenv("_CONDOR_SCRATCH_DIR");
	}
	if (outsidePath.length() > 0) {
		size_t pos = 0;
		do {
			pos = env.find(outsidePath, pos);
			if (pos != std::string::npos) {
				env.replace(pos, outsidePath.length(), insidePath);
				pos += insidePath.length();
			}
		} while (pos != std::string::npos);
	}
	// we probably replaced the value of _CONDOR_SCRATCH_DIR_OUTSIDE_CONTAINER
	// with the inside value, so set that back
	size_t pos = 0;
	pos = env.find("_CONDOR_SCRATCH_DIR_OUTSIDE_CONTAINER=", pos);
	if (pos != std::string::npos) {
		size_t eqpos = env.find("=", pos);
		eqpos++;
		env.replace(eqpos, insidePath.length(), outsidePath);
	}

	// make a vector to hold all the pointers to env entries
	std::vector<const char *> envp;

	// copy DISPLAY from outside to inside.  sshd has set this,
	// it is the only one from the outside we need on the inside,
	// and one way that an ssh-to-job shell is different than the job.
	std::string display;
	if (getenv("DISPLAY")) {
		formatstr(display, "DISPLAY=%s", getenv("DISPLAY"));
		envp.push_back(display.c_str());
	}

	// the first one
	envp.push_back(env.c_str());
	auto it = env.cbegin();
	while (env.cend() != (it = std::find(it, env.cend(), '\0'))) {
		// skip past null terminator
		it++;
		if (& (*it)  != nullptr) {
			envp.push_back(& (*it));
		}
	}
	envp.push_back(nullptr);
	envp.push_back(nullptr);

	std::string filename;

	// start changing namespaces.  Note that once we do this, things
	// get funny in this process
	formatstr(filename, "/proc/%d/ns/uts", pid);
	int fd = open(filename.c_str(), O_RDONLY);
	if (fd < 0) {
		fprintf(stderr, "Can't open uts namespace: %d %s\n", errno, strerror(errno));
		exit(1);
	}
	int r = setns(fd, 0);
	close(fd);
	if (r < 0) {
		// This means an unprivileged singularity, most likely
		// need to set user namespace instead.
		formatstr(filename, "/proc/%d/ns/user", pid);
		fd = open(filename.c_str(), O_RDONLY);
		if (fd < 0) {
			fprintf(stderr, "Can't open user namespace: %d %s\n", errno, strerror(errno));
			exit(1);
		}
		r = setns(fd, 0);
		close(fd);
		if (r < 0) {
			fprintf(stderr, "Can't setns to user namespace: %s\n", strerror(errno));
			exit(1);
		}
	}

	// now the pid namespace
	formatstr(filename, "/proc/%d/ns/pid", pid);
	fd = open(filename.c_str(), O_RDONLY);
	if (fd < 0) {
		fprintf(stderr, "Can't open pid namespace: %s\n", strerror(errno));
		exit(1);
	}
	r = setns(fd, 0);
	close(fd);
	if (r < 0) {
		fprintf(stderr, "Can't setns to pid namespace: %s\n", strerror(errno));
		exit(1);
	}

	// finally the mnt namespace
	formatstr(filename, "/proc/%d/ns/mnt", pid);
	fd = open(filename.c_str(), O_RDONLY);
	if (fd < 0) {
		fprintf(stderr, "Can't open mnt namespace: %s\n", strerror(errno));
		exit(1);
	}
	r = setns(fd, 0);
	close(fd);
	if (r < 0) {
		fprintf(stderr, "Can't setns to mnt namespace: %s\n", strerror(errno));
		exit(1);
	}

	setgroups(0, nullptr);

	// order matters!
	r = setgid(gid);
	if (r < 0) {
		fprintf(stderr, "Can't setgid to %d\n", gid);
		exit(1);
	}
	r = setuid(uid);
	if (r < 0) {
		fprintf(stderr, "Can't setuid to %d\n", uid);
		exit(1);
	}

	struct winsize win;
	ioctl(0, TIOCGWINSZ, &win);

	// now the pty handling
	int masterPty = -1;
	int workerPty = -1;
	masterPty = open("/dev/ptmx", O_RDWR);
	unlockpt(masterPty);

	if (masterPty < 0) {
		fprintf(stderr, "Can't open master pty %s\n", strerror(errno));
		exit(1);
	} else {
		workerPty = open(ptsname(masterPty), O_RDWR);
		if (workerPty < 0) {
			fprintf(stderr, "Can't open worker pty %s\n", strerror(errno));
			exit(1);
		}
	}
	int childpid = fork();
	if (childpid == 0) {

		// in the child --

		close(0);
		close(1);
		close(2);
		close(masterPty);

		dup2(workerPty, 0);
		dup2(workerPty, 1);
		dup2(workerPty, 2);

		if (getenv("_CONDOR_SCRATCH_DIR")) {
			int r = chdir(getenv("_CONDOR_SCRATCH_DIR"));
			if (r < 0) {
				printf("Cannot chdir to %s: %s\n", getenv("_CONDOR_SCRATCH_DIR"), strerror(errno));
			}
 		}

		// make this process group leader so shell job control works
		setsid();

		// Make the pty the controlling terminal
		ioctl(workerPty, TIOCSCTTY, 0);

		// and make it the process group leader
		tcsetpgrp(workerPty, getpid());

		// and set the window size properly
		ioctl(0, TIOCSWINSZ, &win);

		// Finally, launch the shell
		execle("/bin/sh", "/bin/sh", "-l", "-i", nullptr, envp.data());

		// Only get here if exec fails
		fprintf(stderr, "exec failed %d\n", errno);
		exit(errno);

	} else {

		// the parent
		fd_set readfds, writefds, exceptfds;
		bool keepGoing = true;

		// put the pty in raw mode
		struct termios tio;
		tcgetattr(0, &tio);
		pty_is_raw = true;
		old_tio = tio;
		tio.c_iflag &= ~(ICRNL);
		tio.c_oflag &= ~(OPOST);
		tio.c_cflag |= (CS8);
		tio.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
		tio.c_cc[VMIN] = 1;
		tio.c_cc[VTIME] = 0;
		tcsetattr(0, TCSAFLUSH, &tio);

		struct sigaction handler;
		struct sigaction oldhandler;
		handler.sa_handler = reset_pty_and_exit;
		handler.sa_flags   = 0;
		sigemptyset(&handler.sa_mask);

		sigaction(SIGCHLD, &handler, &oldhandler);

		while (keepGoing) {
			FD_ZERO(&readfds);
			FD_ZERO(&writefds);
			FD_ZERO(&exceptfds);

			FD_SET(0, &readfds);
			FD_SET(masterPty, &readfds);

			select(masterPty + 1, &readfds, &writefds, &exceptfds, nullptr);

			if (FD_ISSET(masterPty, &readfds)) {
				char buf[4096];
				int r = read(masterPty, buf, 4096);
				if (r > 0) {
					int ret = write(1, buf, r);
					if (ret < 0) {
						reset_pty_and_exit(0);
					}
				} else {
					keepGoing = false;
				}
			}

			if (FD_ISSET(0, &readfds)) {
				char buf[4096];
				int r = read(0, buf, 4096);
				if (r > 0) {
					int ret = write(masterPty, buf, r);
					if (ret < 0) {
						reset_pty_and_exit(0);
					}
				} else {
					keepGoing = false;
				}
			}
		}
		int status;
		waitpid(childpid, &status, 0);
	}
	return 0;
}

<|endoftext|>


<commit_before>//
// Copyright (c) 2013-2014 Christoph Malek
// See LICENSE for more information.
//

#ifndef RJ_GAME_MAIN_MENU_SITE_HOME_HPP
#define RJ_GAME_MAIN_MENU_SITE_HOME_HPP

#include <rectojump/game/popup_manager.hpp>
#include <rectojump/ui/button.hpp>
#include <rectojump/ui/sf_widget.hpp>
#include <rectojump/ui/stacked_widget.hpp>
#include <rectojump/ui/textbox.hpp>

namespace rj
{
	template<typename Overlay>
	class site_home
	{
		Overlay& m_overlay;
		data_store_type& m_datasore;

		ui::stacked_widget& m_sites;

	public:
		site_home(Overlay& ov) :
			m_overlay{ov},
			m_datasore{m_overlay.mainmenu().gamehandler().datastore()},
			m_sites{ov.sites()}
		{ }

		void construct()
		{
			const auto& font(m_datasore.template get<sf::Font>(glob::text_font));

			// logo
			auto logo_shape(m_sites.add_object<widget::rectangle_shape>("home", vec2f{128.f, 128.f}));
			logo_shape->get().setTexture(&m_datasore.template get<sf::Texture>("rj_logo.png"));
			logo_shape->get().setPosition({(m_sites.bounds().width - logo_shape->get().getSize().x) / 2.f, m_sites.size().y * 0.35f - logo_shape->get().getSize().y});

			// textboxes
			const vec2f tbsize{250.f, 30.f};
			auto username(m_sites.add_object<ui::textbox>("home",
														  tbsize,
														  vec2f{(m_sites.bounds().width - tbsize.x) / 2.f, logo_shape->get().getPosition().y + 140.f},
														  font,
														  "Username"));
			auto password(m_sites.add_object<ui::textbox>("home",
														  tbsize,
														  vec2f{(m_sites.bounds().width - tbsize.x) / 2.f, logo_shape->get().getPosition().y + 180.f},
														  font,
														  "Password"));
			default_textbox(*username);
			default_textbox(*password);
			password->setPasswordMode(true);

			// buttons
			const vec2f btnsize{100.f, 30.f};
			auto login(m_sites.add_object<ui::button>("home", btnsize, vec2f{password->getPosition().x, password->getPosition().y + 40.f}));
			login->set_font(font);
			login->set_text("Login");

			auto register_acc(m_sites.add_object<ui::button>("home", btnsize, vec2f{password->getPosition().x + 150.f, password->getPosition().y + 40.f}));
			register_acc->set_font(font);
			register_acc->set_text("Register");
			register_acc->on_clicked = [this]{m_overlay.mainmenu().gamehandler().popupmgr().template create_popup<popup_type::info>("Register a new account online at: http://");};

			default_button(*login);
			default_button(*register_acc);
		}
	};
}

#endif // RJ_GAME_MAIN_MENU_SITE_HOME_HPP

<commit_msg>game > main_menu > site_home: added version info text<commit_after>//
// Copyright (c) 2013-2014 Christoph Malek
// See LICENSE for more information.
//

#ifndef RJ_GAME_MAIN_MENU_SITE_HOME_HPP
#define RJ_GAME_MAIN_MENU_SITE_HOME_HPP

#include <rectojump/game/popup_manager.hpp>
#include <rectojump/ui/button.hpp>
#include <rectojump/ui/sf_widget.hpp>
#include <rectojump/ui/stacked_widget.hpp>
#include <rectojump/ui/textbox.hpp>

namespace rj
{
	template<typename Overlay>
	class site_home
	{
		Overlay& m_overlay;
		data_store_type& m_datasore;

		ui::stacked_widget& m_sites;

	public:
		site_home(Overlay& ov) :
			m_overlay{ov},
			m_datasore{m_overlay.mainmenu().gamehandler().datastore()},
			m_sites{ov.sites()}
		{ }

		void construct()
		{
			const auto& font(m_datasore.template get<sf::Font>(glob::text_font));

			// logo
			auto logo_shape(m_sites.add_object<widget::rectangle_shape>("home", vec2f{128.f, 128.f}));
			logo_shape->get().setTexture(&m_datasore.template get<sf::Texture>("rj_logo.png"));
			logo_shape->get().setPosition((m_sites.bounds().width - logo_shape->get().getSize().x) / 2.f, m_sites.size().y * 0.35f - logo_shape->get().getSize().y);

			// info text
			auto text(m_sites.add_object<widget::text>("home", "", m_overlay.mainmenu().gamehandler().datastore().template get<sf::Font>(glob::text_font), glob::text_size));
			text->get().setString("version " + glob::get_version());
			text->get().setColor(to_rgb("#373737"));
			text->get().setPosition((m_sites.bounds().width - text->get().getGlobalBounds().width) / 2.f, logo_shape->get().getPosition().y + 120.f);

			// textboxes
			const vec2f tbsize{250.f, 30.f};
			auto username(m_sites.add_object<ui::textbox>("home",
														  tbsize,
														  vec2f{(m_sites.bounds().width - tbsize.x) / 2.f, logo_shape->get().getPosition().y + 180.f},
														  font,
														  "Username"));
			auto password(m_sites.add_object<ui::textbox>("home",
														  tbsize,
														  vec2f{(m_sites.bounds().width - tbsize.x) / 2.f, logo_shape->get().getPosition().y + 220.f},
														  font,
														  "Password"));
			default_textbox(*username);
			default_textbox(*password);
			password->setPasswordMode(true);

			// buttons
			const vec2f btnsize{100.f, 30.f};
			auto login(m_sites.add_object<ui::button>("home", btnsize, vec2f{password->getPosition().x, password->getPosition().y + 40.f}));
			login->setFont(font);
			login->setText("Login");

			auto register_acc(m_sites.add_object<ui::button>("home", btnsize, vec2f{password->getPosition().x + 150.f, password->getPosition().y + 40.f}));
			register_acc->setFont(font);
			register_acc->setText("Register");
			register_acc->on_clicked = [this]{m_overlay.mainmenu().gamehandler().popupmgr().template create_popup<popup_type::info>("Register a new account online at: http://");};

			default_button(*login);
			default_button(*register_acc);
		}
	};
}

#endif // RJ_GAME_MAIN_MENU_SITE_HOME_HPP

<|endoftext|>


<commit_before>/***************************Copyright-DO-NOT-REMOVE-THIS-LINE**
 * CONDOR Copyright Notice
 *
 * See LICENSE.TXT for additional notices and disclaimers.
 *
 * Copyright (c)1990-1998 CONDOR Team, Computer Sciences Department,
 * University of Wisconsin-Madison, Madison, WI.  All Rights Reserved.
 * No use of the CONDOR Software Program Source Code is authorized
 * without the express consent of the CONDOR Team.  For more information
 * contact: CONDOR Team, Attention: Professor Miron Livny,
 * 7367 Computer Sciences, 1210 W. Dayton St., Madison, WI 53706-1685,
 * (608) 262-0856 or miron@cs.wisc.edu.
 *
 * U.S. Government Rights Restrictions: Use, duplication, or disclosure
 * by the U.S. Government is subject to restrictions as set forth in
 * subparagraph (c)(1)(ii) of The Rights in Technical Data and Computer
 * Software clause at DFARS 252.227-7013 or subparagraphs (c)(1) and
 * (2) of Commercial Computer Software-Restricted Rights at 48 CFR
 * 52.227-19, as applicable, CONDOR Team, Attention: Professor Miron
 * Livny, 7367 Computer Sciences, 1210 W. Dayton St., Madison,
 * WI 53706-1685, (608) 262-0856 or miron@cs.wisc.edu.
****************************Copyright-DO-NOT-REMOVE-THIS-LINE**/

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

#include "condor_common.h"
#include <iostream.h>
#include "condor_commands.h"
#include "condor_config.h"
#include "condor_classad.h"
#include "condor_debug.h"
#include "condor_io.h"
#include "TimeClass.h"
#include "MyString.h"

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

struct LineRec {
  MyString Name;
  float Priority;
  int Res;
};

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

static void usage(char* name);
static void ProcessInfo(AttrList* ad);
static int CountElem(AttrList* ad);
static void CollectInfo(AttrList* ad, LineRec* LR);
static void PrintInfo(AttrList* ad, LineRec* LR, int NumElem);

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

extern "C" {
int CompPrio(LineRec* a, LineRec* b);
}

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

main(int argc, char* argv[])
{
  int SetMode=0;

  if (argc>1) { // set priority
    if (argc!=4 || strcmp(argv[1],"-set")!=0 ) {
      usage( argv[0] );
      exit(1);
    }
    SetMode=1;
  }

  config( 0 );
  char* NegotiatorHost = param( "NEGOTIATOR_HOST" );
  if (!NegotiatorHost) {
    fprintf( stderr, "No NegotiatorHost host specified in config file\n" );
	exit(1);
  }

  if (SetMode) { // set priority

    char* tmp;
	if( ! (tmp = strchr(argv[2], '@')) ) {
		fprintf( stderr,
				 "%s: You must specify the full name of the submittor you wish\n",
				 argv[0] );
		fprintf( stderr, "\tto update the priority of (%s or %s)\n",
				 "user@uid.domain", "user@full.host.name" );
		exit(1);
	}
    double Priority=atof(argv[3]);

    // send request
    ReliSock sock(NegotiatorHost, NEGOTIATOR_PORT);
    sock.encode();
    if (!sock.put(SET_PRIORITY) ||
        !sock.put(argv[2]) ||
        !sock.put(Priority) ||
        !sock.end_of_message()) {
      fprintf( stderr, "failed to send SET_PRIORITY command to negotiator\n" );
      exit(1);
    }

  }

  else {  // list priorities

    // send request
    ReliSock sock(NegotiatorHost, NEGOTIATOR_PORT);
    sock.encode();
    if (!sock.put(GET_PRIORITY) ||
        !sock.end_of_message()) {
      fprintf( stderr, "failed to send GET_PRIORITY command to negotiator\n" );
      exit(1);
    }

    // get reply
    sock.decode();
    AttrList* ad=new AttrList();
    if (!ad->get(sock) ||
        !sock.end_of_message()) {
      fprintf( stderr, "failed to get classad from negotiator\n" );
      exit(1);
    }

    ProcessInfo(ad);
  }

  exit(0);
}

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

static void ProcessInfo(AttrList* ad)
{
  int NumElem=CountElem(ad);
  LineRec* LR=new LineRec[NumElem];
  CollectInfo(ad,LR);
  qsort(LR,NumElem,sizeof(LineRec),CompPrio);
  PrintInfo(ad,LR,NumElem);
}

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

static int CountElem(AttrList* ad)
{
  int count=0;
  ad->ResetExpr();
  while(ad->NextExpr()) count++;
  return (count-1)/3;
}

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

extern "C" {
int CompPrio(LineRec* a, LineRec* b)
{
  if (a->Priority>b->Priority) return 1;
  return -1;
}
}

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

static void CollectInfo(AttrList* ad, LineRec* LR)
{
  ExprTree* exp;
  ad->ResetExpr();
  exp=ad->NextExpr();
  int i=0;
  while(exp=ad->NextExpr()) {
    LR[i].Name=((String*) exp->RArg())->Value();
    LR[i].Priority=((Float*) ad->NextExpr()->RArg())->Value();
    LR[i].Res=((Integer*) ad->NextExpr()->RArg())->Value();
    i++;
  }

  return;
}

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

static void PrintInfo(AttrList* ad, LineRec* LR, int NumElem)
{
  // ad->AttrList::fPrint(stdout);
  ExprTree* exp;
  ad->ResetExpr();
  exp=ad->NextExpr();
  Time T=((Integer*) exp->RArg())->Value();
  // printf("%f\n",d);
  printf("Last Priority Update: %s\n",T.Asc());

  char* Fmt1="%-30s   %12.3f   %4d\n";
  char* Fmt2="%-30s   %12s   %4s\n";
  char* Fmt3="%-30s   %12s   %4d\n";

  printf(Fmt2,"Name","Priority","Resources Used");
  printf(Fmt2,"----","--------","--------------");

  int Res=0;
  for (int i=0; i<NumElem; i++) {
    printf(Fmt1,(const char*)LR[i].Name,LR[i].Priority,LR[i].Res);
	Res+=LR[i].Res;
  }

  // printf(Fmt2,"----","--------","--------------");
  // printf(Fmt3,"Total","",Res);

  return;
}

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

static void usage(char* name) {
  fprintf( stderr, "usage: %s [ -set user priority_value ]\n", name );
}

<commit_msg>Fixed condor_userprio to not assume attribute ordering.  (This implementation assumes the "NumSubmittors" attribute fix in the accounting module.)<commit_after>/***************************Copyright-DO-NOT-REMOVE-THIS-LINE**
 * CONDOR Copyright Notice
 *
 * See LICENSE.TXT for additional notices and disclaimers.
 *
 * Copyright (c)1990-1998 CONDOR Team, Computer Sciences Department,
 * University of Wisconsin-Madison, Madison, WI.  All Rights Reserved.
 * No use of the CONDOR Software Program Source Code is authorized
 * without the express consent of the CONDOR Team.  For more information
 * contact: CONDOR Team, Attention: Professor Miron Livny,
 * 7367 Computer Sciences, 1210 W. Dayton St., Madison, WI 53706-1685,
 * (608) 262-0856 or miron@cs.wisc.edu.
 *
 * U.S. Government Rights Restrictions: Use, duplication, or disclosure
 * by the U.S. Government is subject to restrictions as set forth in
 * subparagraph (c)(1)(ii) of The Rights in Technical Data and Computer
 * Software clause at DFARS 252.227-7013 or subparagraphs (c)(1) and
 * (2) of Commercial Computer Software-Restricted Rights at 48 CFR
 * 52.227-19, as applicable, CONDOR Team, Attention: Professor Miron
 * Livny, 7367 Computer Sciences, 1210 W. Dayton St., Madison,
 * WI 53706-1685, (608) 262-0856 or miron@cs.wisc.edu.
****************************Copyright-DO-NOT-REMOVE-THIS-LINE**/

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

#include "condor_common.h"
#include <iostream.h>
#include "condor_commands.h"
#include "condor_config.h"
#include "condor_classad.h"
#include "condor_debug.h"
#include "condor_io.h"
#include "TimeClass.h"
#include "MyString.h"

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

struct LineRec {
  MyString Name;
  float Priority;
  int Res;
};

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

static void usage(char* name);
static void ProcessInfo(AttrList* ad);
static int CountElem(AttrList* ad);
static void CollectInfo(int numElem, AttrList* ad, LineRec* LR);
static void PrintInfo(AttrList* ad, LineRec* LR, int NumElem);

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

extern "C" {
int CompPrio(LineRec* a, LineRec* b);
}

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

main(int argc, char* argv[])
{
  int SetMode=0;

  if (argc>1) { // set priority
    if (argc!=4 || strcmp(argv[1],"-set")!=0 ) {
      usage( argv[0] );
      exit(1);
    }
    SetMode=1;
  }

  config( 0 );
  char* NegotiatorHost = param( "NEGOTIATOR_HOST" );
  if (!NegotiatorHost) {
    fprintf( stderr, "No NegotiatorHost host specified in config file\n" );
	exit(1);
  }

  if (SetMode) { // set priority

    char* tmp;
	if( ! (tmp = strchr(argv[2], '@')) ) {
		fprintf( stderr,
				 "%s: You must specify the full name of the submittor you wish\n",
				 argv[0] );
		fprintf( stderr, "\tto update the priority of (%s or %s)\n",
				 "user@uid.domain", "user@full.host.name" );
		exit(1);
	}
    double Priority=atof(argv[3]);

    // send request
    ReliSock sock(NegotiatorHost, NEGOTIATOR_PORT);
    sock.encode();
    if (!sock.put(SET_PRIORITY) ||
        !sock.put(argv[2]) ||
        !sock.put(Priority) ||
        !sock.end_of_message()) {
      fprintf( stderr, "failed to send SET_PRIORITY command to negotiator\n" );
      exit(1);
    }

  }

  else {  // list priorities

    // send request
    ReliSock sock(NegotiatorHost, NEGOTIATOR_PORT);
    sock.encode();
    if (!sock.put(GET_PRIORITY) ||
        !sock.end_of_message()) {
      fprintf( stderr, "failed to send GET_PRIORITY command to negotiator\n" );
      exit(1);
    }

    // get reply
    sock.decode();
    AttrList* ad=new AttrList();
    if (!ad->get(sock) ||
        !sock.end_of_message()) {
      fprintf( stderr, "failed to get classad from negotiator\n" );
      exit(1);
    }

    ProcessInfo(ad);
  }

  exit(0);
}

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

static void ProcessInfo(AttrList* ad)
{
  int NumElem=CountElem(ad);
  LineRec* LR=new LineRec[NumElem];
  CollectInfo(NumElem,ad,LR);
  qsort(LR,NumElem,sizeof(LineRec),CompPrio);
  PrintInfo(ad,LR,NumElem);
}

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

static int CountElem(AttrList* ad)
{
	int numSubmittors;
	if( ad->LookupInteger( "NumSubmittors", numSubmittors ) )
		return numSubmittors;
	else
		return -1;
}

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

extern "C" {
int CompPrio(LineRec* a, LineRec* b)
{
  if (a->Priority>b->Priority) return 1;
  return -1;
}
}

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

static void CollectInfo(int numElem, AttrList* ad, LineRec* LR)
{
  char  attrName[32], attrPrio[32], attrResUsed[32];
  char  name[128];
  float priority;
  int   resUsed;

  for( int i=1; i<=numElem; i++) {
    sprintf( attrName , "Name%d", i );
    sprintf( attrPrio , "Priority%d", i );
    sprintf( attrResUsed , "ResourcesUsed%d", i );

    if( !ad->LookupString	( attrName, name ) 		||
		!ad->LookupFloat	( attrPrio, priority )	||
		!ad->LookupInteger	( attrResUsed, resUsed ) )
			continue;

    LR[i-1].Name=name;
    LR[i-1].Priority=priority;
    LR[i-1].Res=resUsed;
  }

  return;
}

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

static void PrintInfo(AttrList* ad, LineRec* LR, int NumElem)
{
  ExprTree* exp;
  ad->ResetExpr();
  exp=ad->NextExpr();
  Time T=((Integer*) exp->RArg())->Value();
  printf("Last Priority Update: %s\n",T.Asc());

  char* Fmt1="%-30s   %12.3f   %4d\n";
  char* Fmt2="%-30s   %12s   %4s\n";
  char* Fmt3="%-30s   %12s   %4d\n";

  printf(Fmt2,"Name","Priority","Resources Used");
  printf(Fmt2,"----","--------","--------------");

  int Res=0;
  for (int i=0; i<NumElem; i++) {
    printf(Fmt1,(const char*)LR[i].Name,LR[i].Priority,LR[i].Res);
	Res+=LR[i].Res;
  }

  // printf(Fmt2,"----","--------","--------------");
  // printf(Fmt3,"Total","",Res);

  return;
}

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

static void usage(char* name) {
  fprintf( stderr, "usage: %s [ -set user priority_value ]\n", name );
}

<|endoftext|>