<commit_before>#include "Codegen.h"
#include "ASTNode.h"
#include "lib/Runtime.h"
#include "Internal.h"
#include <stdexcept>
#include <vector>
using namespace std;

#define __ m_Asm->
#define e__ entry_asm->

namespace snow {
namespace x86_64 {
	// stack_frame->`member'
	#define GET_STACK(member) (Address(rbp, (-(int)sizeof(StackFrame))+(int)offsetof(StackFrame, member)))

	// temporaries[id]
	#define GET_TEMPORARY(id) (Address(rbp, (-(int)sizeof(StackFrame))-(sizeof(VALUE)*(id+1))))

	// reg[index] (reg must contain a pointer to an array of values)
	#define GET_ARRAY_PTR(reg, index) (Address((reg), index * sizeof(VALUE)))

	static const Register* arg_regs[] = { &rdi, &rsi, &rdx, &rcx, &r8, &r9 };
	static const uint64_t num_arg_regs = 6;

	Codegen::Codegen(ast::FunctionDefinition& def) :
		snow::Codegen(def),
		m_NumLocals(0),
		m_NumStackArguments(0),
		m_NumTemporaries(0) {
		m_LocalMap = new(kGarbage) LocalMap;
		m_Asm = new x86_64::Assembler;
		m_Return = new Label;
	}

	uint64_t Codegen::reserve_temporary() {
		if (m_FreeTemporaries.size() > 0) {
			uint64_t t = m_FreeTemporaries.back();
			m_FreeTemporaries.pop_back();
			return t;
		}
		return m_NumTemporaries++;
	}

	void Codegen::free_temporary(uint64_t id) {
		m_FreeTemporaries.push_back(id);
	}

	void Codegen::get_local(uint64_t id, const Register& reg) {
		__ mov(GET_STACK(locals), reg);
		__ mov(GET_ARRAY_PTR(reg, id), reg);
	}

	void Codegen::set_local(const Register& reg, uint64_t id, const Register& tmp) {
		assert(reg != tmp && "Cannot use source register as temporary storage!");
		__ mov(GET_STACK(locals), tmp);
		__ mov(reg, GET_ARRAY_PTR(tmp, id));
	}

	RefPtr<CompiledCode> Codegen::compile() {
		RefPtr<x86_64::Assembler> entry_asm = new x86_64::Assembler;
		__ subasm(entry_asm);

		if (m_Def.arguments.size() > 0) {
			__ comment("copy arguments to locals");
			__ mov(GET_STACK(arguments), r8);
			__ mov(GET_STACK(locals), r9);
			size_t i = 0;
			for each (iter, m_Def.arguments) {
				auto local = m_LocalMap->define_local((*iter)->name);
				__ mov(GET_ARRAY_PTR(r8, i), rax);
				__ mov(rax, GET_ARRAY_PTR(r9, local));
				++i;
			}
		}

		__ comment("function body");
		compile(*m_Def.sequence);

		uint64_t return_temporary = reserve_temporary();
		__ comment("function exit");
		__ bind(m_Return);
		__ mov(rax, GET_TEMPORARY(return_temporary));
		__ call("snow_leave_scope");
		__ mov(GET_TEMPORARY(return_temporary), rax);
		__ leave();
		__ ret();

		// Compile the function entry, now that we know all the locals and
		// temporaries.
		{
			e__ comment("function entry");
			int stack_size = sizeof(StackFrame) + sizeof(VALUE)*m_NumTemporaries;
			// maintain 16-byte stack alignment
			stack_size += stack_size % 16;
			e__ enter(stack_size);

			e__ mov(rbp, rsi);
			e__ sub(sizeof(StackFrame), rsi);
			e__ call("snow_enter_scope");
		}

		RefPtr<CompiledCode> code = __ compile();
		code->set_local_map(m_LocalMap);
		for each (iter, m_Related) {
			code->add_related(*iter);
		}
		return code;
	}

	void Codegen::compile(ast::Literal& literal) {
		__ comment(std::string("literal: `")  + literal.string + std::string("'"));
		using ast::Literal;

		const char* str = literal.string.c_str();

		VALUE val = nil();

		switch (literal.type) {
			case Literal::INTEGER_DEC_TYPE:
				val = value(strtoll(str, NULL, 10));
				break;
			case Literal::INTEGER_HEX_TYPE:
				val = value(strtoll(str, NULL, 16));
				break;
			case Literal::INTEGER_BIN_TYPE:
				val = value(strtoll(str, NULL, 2));
				break;
			case Literal::FLOAT_TYPE:
				// TODO: Doubles
				val = value(strtof(str, NULL));
				break;
			case Literal::STRING_TYPE:
				val = create_string(str);
				break;
			case Literal::TRUE_TYPE:
				val = value(true);
				break;
			case Literal::FALSE_TYPE:
				val = value(false);
				break;
			case Literal::NIL_TYPE:
				val = nil();
                break;
		}

		__ mov(val, rax);
	}

	void Codegen::compile(ast::Identifier& id) {
		__ comment(std::string("identifier: `") + id.name + std::string("'"));
		if (m_LocalMap->has_local(id.name)) {
			// It's a local from current scope...
			get_local(m_LocalMap->local(id.name), rax);
		} else {
			// THE PAIN! It's from a parent scope...
			__ mov(rbp, rdi);
			__ sub(sizeof(StackFrame), rdi);
			// TODO/XXX: Symbol storage -- id.name is freed at some point.
			__ mov(id.name.c_str(), rsi);
			__ mov(id.quiet, rdx);
			__ call("snow_get_local");
		}
	}

	void Codegen::compile(ast::Sequence& seq) {
		for each (iter, seq.nodes) {
			(*iter)->compile(*this);
		}
	}

	void Codegen::compile(ast::FunctionDefinition& def) {
		__ comment("function definition");
		RefPtr<Codegen> codegen = new Codegen(def);
		RefPtr<CompiledCode> code = codegen->compile();
		m_Related.push_back(code);
		VALUE func = new Function(*code);
		__ mov(func, rdi);
		__ mov(GET_STACK(scope), rsi);
		__ call("snow_set_parent_scope");
		__ mov(func, rax);
	}

	void Codegen::compile(ast::Return& ret) {
		__ comment("return");
		if (ret.expression)
			ret.expression->compile(*this);
		else
			__ clear(rax);
		__ jmp(m_Return);
	}

	void Codegen::compile(ast::Assignment& assign) {
		uint64_t l;
		if (m_LocalMap->has_local(assign.identifier->name))
			l = m_LocalMap->local(assign.identifier->name);
		else
			l = m_LocalMap->define_local(assign.identifier->name);

		assign.expression->compile(*this);
		__ comment(std::string("assignment: ") + assign.identifier->name);
		set_local(rax, l);
	}

	void Codegen::compile(ast::IfCondition& cond) {
		RefPtr<Label> test_cond = new Label;
		RefPtr<Label> after = new Label;

		__ bind(test_cond);
		__ comment("if cond");
		cond.expression->compile(*this);
		__ mov(rax, rdi);
		__ call("snow_eval_truth");
		__ cmp(0, rax);
		__ j(CC_EQUAL, after);
		__ comment("if body");
		cond.if_true->compile(*this);
		__ bind(after);
	}

	void Codegen::compile(ast::IfElseCondition& cond) {
		RefPtr<Label> test_cond = new Label;
		RefPtr<Label> if_true = new Label;
		RefPtr<Label> if_false = new Label;
		RefPtr<Label> after = new Label;

		__ bind(test_cond);
		__ comment("if-else cond");
		cond.expression->compile(*this);
		__ mov(rax, rdi);
		__ call("snow_eval_truth");
		__ cmp(0, rax);
		__ j(CC_EQUAL, if_false);
		__ bind(if_true);
		__ comment("if true");
		cond.if_true->compile(*this);
		__ jmp(after);
		__ bind(if_false);
		__ comment("if false");
		cond.if_false->compile(*this);
		__ bind(after);
	}

	void Codegen::compile(ast::Call& call) {
		auto self_tmp = reserve_temporary();

		__ comment("self for call");
		call.self->compile(*this);
		__ mov(rax, GET_TEMPORARY(self_tmp));

		// evaluate arguments and store temporaries
		auto num_args = call.arguments->length();
		uint64_t args_tmp[num_args];
		size_t i = 0;
		for each (arg_iter, call.arguments->nodes) {
			args_tmp[i] = reserve_temporary();
			__ comment(string_printf("argument %d", i));
			(*arg_iter)->compile(*this);
			__ mov(rax, GET_TEMPORARY(args_tmp[i]));
			++i;
		}

		auto function_tmp = self_tmp;
		if (call.member) {
			__ comment("method call");
			function_tmp = reserve_temporary();
			__ mov(GET_TEMPORARY(self_tmp), rdi);
			__ mov(call.member->name.c_str(), rsi);
			__ call("snow_get");
			__ mov(rax, GET_TEMPORARY(function_tmp));
			__ mov(GET_TEMPORARY(self_tmp), rdi);
		} else {
			__ comment("closure call");
			__ clear(rdi);         // "self" is NULL for closures
		}

		__ mov(GET_TEMPORARY(function_tmp), rsi);
		__ mov(num_args, rdx);

		for (size_t i = 0; i < num_args; ++i) {
			const size_t arg_offset = i + 3;

			if (arg_offset < num_arg_regs) {
				__ mov(GET_TEMPORARY(args_tmp[i]), *arg_regs[arg_offset]);
			} else {
				// TODO!
			}

			free_temporary(args_tmp[i]);
		}

		if (self_tmp == function_tmp)
			free_temporary(self_tmp);
		else {
			free_temporary(self_tmp);
			free_temporary(function_tmp);
		}

		// finally!
		__ clear(rax);
		__ call("snow_call");
	}

	void Codegen::compile(ast::Get& get) {
		__ comment("get `" + get.member->name + "'");
		get.self->compile(*this);
		__ mov(rax, rdi);
		__ mov(get.member->name.c_str(), rsi);
		__ call("snow_get");
	}

	void Codegen::compile(ast::Set& set) {
		__ comment("set `" + set.member->name + "'");
		set.expression->compile(*this);
		auto tmp = reserve_temporary();
		__ mov(rax, GET_TEMPORARY(tmp));
		set.self->compile(*this);
		__ mov(rax, rdi);
		__ mov(set.member->name.c_str(), rsi);
		__ mov(GET_TEMPORARY(tmp), rdx);
		__ call("snow_set");
		free_temporary(tmp);
	}

	void Codegen::compile(ast::Loop& loop) {
		RefPtr<Label> test_cond = new Label;
		RefPtr<Label> body = new Label;
		RefPtr<Label> after = new Label;

		__ bind(test_cond);
		__ comment("loop cond");
		loop.expression->compile(*this);
		__ mov(rax, rdi);
		__ call("snow_eval_truth");
		__ cmp(0, rax);
		__ j(CC_EQUAL, after);
		__ bind(body);
		__ comment("loop body");
		loop.while_true->compile(*this);
		__ jmp(test_cond);
		__ bind(after);
	}

}
}<commit_msg>Method calls with more than 3 arguments now work!<commit_after>#include "Codegen.h"
#include "ASTNode.h"
#include "lib/Runtime.h"
#include "Internal.h"
#include <stdexcept>
#include <vector>
using namespace std;

#define __ m_Asm->
#define e__ entry_asm->

namespace snow {
namespace x86_64 {
	// stack_frame->`member'
	#define GET_STACK(member) (Address(rbp, (-(int)sizeof(StackFrame))+(int)offsetof(StackFrame, member)))

	// temporaries[id]
	#define GET_TEMPORARY(id) (Address(rbp, (-(int)sizeof(StackFrame))-(sizeof(VALUE)*(id+1))))

	// reg[index] (reg must contain a pointer to an array of values)
	#define GET_ARRAY_PTR(reg, index) (Address((reg), index * sizeof(VALUE)))

	static const Register* arg_regs[] = { &rdi, &rsi, &rdx, &rcx, &r8, &r9 };
	static const uint64_t num_arg_regs = 6;

	Codegen::Codegen(ast::FunctionDefinition& def) :
		snow::Codegen(def),
		m_NumLocals(0),
		m_NumStackArguments(0),
		m_NumTemporaries(0) {
		m_LocalMap = new(kGarbage) LocalMap;
		m_Asm = new x86_64::Assembler;
		m_Return = new Label;
	}

	uint64_t Codegen::reserve_temporary() {
		if (m_FreeTemporaries.size() > 0) {
			uint64_t t = m_FreeTemporaries.back();
			m_FreeTemporaries.pop_back();
			return t;
		}
		return m_NumTemporaries++;
	}

	void Codegen::free_temporary(uint64_t id) {
		m_FreeTemporaries.push_back(id);
	}

	void Codegen::get_local(uint64_t id, const Register& reg) {
		__ mov(GET_STACK(locals), reg);
		__ mov(GET_ARRAY_PTR(reg, id), reg);
	}

	void Codegen::set_local(const Register& reg, uint64_t id, const Register& tmp) {
		assert(reg != tmp && "Cannot use source register as temporary storage!");
		__ mov(GET_STACK(locals), tmp);
		__ mov(reg, GET_ARRAY_PTR(tmp, id));
	}

	RefPtr<CompiledCode> Codegen::compile() {
		RefPtr<x86_64::Assembler> entry_asm = new x86_64::Assembler;
		__ subasm(entry_asm);

		if (m_Def.arguments.size() > 0) {
			__ comment("copy arguments to locals");
			__ mov(GET_STACK(arguments), r8);
			__ mov(GET_STACK(locals), r9);
			size_t i = 0;
			for each (iter, m_Def.arguments) {
				auto local = m_LocalMap->define_local((*iter)->name);
				__ mov(GET_ARRAY_PTR(r8, i), rax);
				__ mov(rax, GET_ARRAY_PTR(r9, local));
				++i;
			}
		}

		__ comment("function body");
		compile(*m_Def.sequence);

		uint64_t return_temporary = reserve_temporary();
		__ comment("function exit");
		__ bind(m_Return);
		__ mov(rax, GET_TEMPORARY(return_temporary));
		__ call("snow_leave_scope");
		__ mov(GET_TEMPORARY(return_temporary), rax);
		__ leave();
		__ ret();

		// Compile the function entry, now that we know all the locals and
		// temporaries.
		{
			e__ comment("function entry");
			int stack_size = sizeof(StackFrame) + sizeof(VALUE)*(m_NumTemporaries + m_NumStackArguments);
			// maintain 16-byte stack alignment
			stack_size += stack_size % 16;
			e__ enter(stack_size);

			e__ mov(rbp, rsi);
			e__ sub(sizeof(StackFrame), rsi);
			e__ call("snow_enter_scope");
		}

		RefPtr<CompiledCode> code = __ compile();
		code->set_local_map(m_LocalMap);
		for each (iter, m_Related) {
			code->add_related(*iter);
		}
		return code;
	}

	void Codegen::compile(ast::Literal& literal) {
		__ comment(std::string("literal: `")  + literal.string + std::string("'"));
		using ast::Literal;

		const char* str = literal.string.c_str();

		VALUE val = nil();

		switch (literal.type) {
			case Literal::INTEGER_DEC_TYPE:
				val = value(strtoll(str, NULL, 10));
				break;
			case Literal::INTEGER_HEX_TYPE:
				val = value(strtoll(str, NULL, 16));
				break;
			case Literal::INTEGER_BIN_TYPE:
				val = value(strtoll(str, NULL, 2));
				break;
			case Literal::FLOAT_TYPE:
				// TODO: Doubles
				val = value(strtof(str, NULL));
				break;
			case Literal::STRING_TYPE:
				val = create_string(str);
				break;
			case Literal::TRUE_TYPE:
				val = value(true);
				break;
			case Literal::FALSE_TYPE:
				val = value(false);
				break;
			case Literal::NIL_TYPE:
				val = nil();
                break;
		}

		__ mov(val, rax);
	}

	void Codegen::compile(ast::Identifier& id) {
		__ comment(std::string("identifier: `") + id.name + std::string("'"));
		if (m_LocalMap->has_local(id.name)) {
			// It's a local from current scope...
			get_local(m_LocalMap->local(id.name), rax);
		} else {
			// THE PAIN! It's from a parent scope...
			__ mov(rbp, rdi);
			__ sub(sizeof(StackFrame), rdi);
			// TODO/XXX: Symbol storage -- id.name is freed at some point.
			__ mov(id.name.c_str(), rsi);
			__ mov(id.quiet, rdx);
			__ call("snow_get_local");
		}
	}

	void Codegen::compile(ast::Sequence& seq) {
		for each (iter, seq.nodes) {
			(*iter)->compile(*this);
		}
	}

	void Codegen::compile(ast::FunctionDefinition& def) {
		__ comment("function definition");
		RefPtr<Codegen> codegen = new Codegen(def);
		RefPtr<CompiledCode> code = codegen->compile();
		m_Related.push_back(code);
		VALUE func = new Function(*code);
		__ mov(func, rdi);
		__ mov(GET_STACK(scope), rsi);
		__ call("snow_set_parent_scope");
		__ mov(func, rax);
	}

	void Codegen::compile(ast::Return& ret) {
		__ comment("return");
		if (ret.expression)
			ret.expression->compile(*this);
		else
			__ clear(rax);
		__ jmp(m_Return);
	}

	void Codegen::compile(ast::Assignment& assign) {
		uint64_t l;
		if (m_LocalMap->has_local(assign.identifier->name))
			l = m_LocalMap->local(assign.identifier->name);
		else
			l = m_LocalMap->define_local(assign.identifier->name);

		assign.expression->compile(*this);
		__ comment(std::string("assignment: ") + assign.identifier->name);
		set_local(rax, l);
	}

	void Codegen::compile(ast::IfCondition& cond) {
		RefPtr<Label> test_cond = new Label;
		RefPtr<Label> after = new Label;

		__ bind(test_cond);
		__ comment("if cond");
		cond.expression->compile(*this);
		__ mov(rax, rdi);
		__ call("snow_eval_truth");
		__ cmp(0, rax);
		__ j(CC_EQUAL, after);
		__ comment("if body");
		cond.if_true->compile(*this);
		__ bind(after);
	}

	void Codegen::compile(ast::IfElseCondition& cond) {
		RefPtr<Label> test_cond = new Label;
		RefPtr<Label> if_true = new Label;
		RefPtr<Label> if_false = new Label;
		RefPtr<Label> after = new Label;

		__ bind(test_cond);
		__ comment("if-else cond");
		cond.expression->compile(*this);
		__ mov(rax, rdi);
		__ call("snow_eval_truth");
		__ cmp(0, rax);
		__ j(CC_EQUAL, if_false);
		__ bind(if_true);
		__ comment("if true");
		cond.if_true->compile(*this);
		__ jmp(after);
		__ bind(if_false);
		__ comment("if false");
		cond.if_false->compile(*this);
		__ bind(after);
	}

	void Codegen::compile(ast::Call& call) {
		auto self_tmp = reserve_temporary();

		__ comment("self for call");
		call.self->compile(*this);
		__ mov(rax, GET_TEMPORARY(self_tmp));

		// evaluate arguments and store temporaries
		auto num_args = call.arguments->length();
		uint64_t args_tmp[num_args];
		size_t i = 0;
		for each (arg_iter, call.arguments->nodes) {
			args_tmp[i] = reserve_temporary();
			__ comment(string_printf("argument %d", i));
			(*arg_iter)->compile(*this);
			__ mov(rax, GET_TEMPORARY(args_tmp[i]));
			++i;
		}

		auto function_tmp = self_tmp;
		if (call.member) {
			__ comment("method call");
			function_tmp = reserve_temporary();
			__ mov(GET_TEMPORARY(self_tmp), rdi);
			__ mov(call.member->name.c_str(), rsi);
			__ call("snow_get");
			__ mov(rax, GET_TEMPORARY(function_tmp));
			__ mov(GET_TEMPORARY(self_tmp), rdi);
		} else {
			__ comment("closure call");
			__ clear(rdi);         // "self" is NULL for closures
		}

		__ mov(GET_TEMPORARY(function_tmp), rsi);
		__ mov(num_args, rdx);

		for (size_t i = 0; i < num_args; ++i) {
			const size_t arg_offset = i + 3;

			if (arg_offset < num_arg_regs) {
				__ mov(GET_TEMPORARY(args_tmp[i]), *arg_regs[arg_offset]);
			} else {
				const size_t stack_offset = arg_offset - num_arg_regs;
				if (m_NumStackArguments < stack_offset)
					m_NumStackArguments = stack_offset;
				__ mov(GET_TEMPORARY(args_tmp[i]), rax);
				// XXX: Can this be done without SIB?
				__ mov(stack_offset, rbx);
				__ mov(rax, SIB(rsp, rbx, SIB::SCALE_8));
			}

			free_temporary(args_tmp[i]);
		}

		if (self_tmp == function_tmp)
			free_temporary(self_tmp);
		else {
			free_temporary(self_tmp);
			free_temporary(function_tmp);
		}

		// finally!
		__ clear(rax);
		__ call("snow_call");
	}

	void Codegen::compile(ast::Get& get) {
		__ comment("get `" + get.member->name + "'");
		get.self->compile(*this);
		__ mov(rax, rdi);
		__ mov(get.member->name.c_str(), rsi);
		__ call("snow_get");
	}

	void Codegen::compile(ast::Set& set) {
		__ comment("set `" + set.member->name + "'");
		set.expression->compile(*this);
		auto tmp = reserve_temporary();
		__ mov(rax, GET_TEMPORARY(tmp));
		set.self->compile(*this);
		__ mov(rax, rdi);
		__ mov(set.member->name.c_str(), rsi);
		__ mov(GET_TEMPORARY(tmp), rdx);
		__ call("snow_set");
		free_temporary(tmp);
	}

	void Codegen::compile(ast::Loop& loop) {
		RefPtr<Label> test_cond = new Label;
		RefPtr<Label> body = new Label;
		RefPtr<Label> after = new Label;

		__ bind(test_cond);
		__ comment("loop cond");
		loop.expression->compile(*this);
		__ mov(rax, rdi);
		__ call("snow_eval_truth");
		__ cmp(0, rax);
		__ j(CC_EQUAL, after);
		__ bind(body);
		__ comment("loop body");
		loop.while_true->compile(*this);
		__ jmp(test_cond);
		__ bind(after);
	}

}
}<|endoftext|>


<commit_before>//------------------------------------------------------------------------------
//  Draw.cc
//------------------------------------------------------------------------------
#include "Draw.h"
#include "yakc_shaders.h"

using namespace Oryol;

namespace YAKC {

//------------------------------------------------------------------------------
void
Draw::Setup(const GfxSetup& gfxSetup, int frame_x, int frame_y) {
    this->crtEffectEnabled = false;
    this->crtColorEnabled = true;
    this->frameSizeX = frame_x;
    this->frameSizeY = frame_y;
    this->texUpdateAttrs.NumFaces = 1;
    this->texUpdateAttrs.NumMipMaps = 1;
    this->texUpdateAttrs.Sizes[0][0] = 0;

    auto fsqSetup = MeshSetup::FullScreenQuad(true);
    Id fsq = Gfx::CreateResource(fsqSetup);
    this->crtDrawState.Mesh[0] = fsq;
    this->nocrtDrawState.Mesh[0] = fsq;

    Id crtShd = Gfx::CreateResource(CRTShader::Setup());
    Id nocrtShd = Gfx::CreateResource(NoCRTShader::Setup());
    auto pips = PipelineSetup::FromLayoutAndShader(fsqSetup.Layout, crtShd);
    pips.DepthStencilState.DepthWriteEnabled = false;
    pips.DepthStencilState.DepthCmpFunc = CompareFunc::Always;
    pips.BlendState.ColorFormat = gfxSetup.ColorFormat;
    pips.BlendState.DepthFormat = gfxSetup.DepthFormat;
    pips.RasterizerState.SampleCount = gfxSetup.SampleCount;
    this->crtDrawState.Pipeline = Gfx::CreateResource(pips);
    pips.Shader = nocrtShd;
    this->nocrtDrawState.Pipeline = Gfx::CreateResource(pips);
}

//------------------------------------------------------------------------------
void
Draw::Discard() {
    // nothing to do here
}

//------------------------------------------------------------------------------
void
Draw::UpdateParams(bool enableCrtEffect, bool colorTV, const glm::vec2& warp) {
    this->crtEffectEnabled = enableCrtEffect;
    this->crtColorEnabled = colorTV;
    this->crtWarp = warp;
}

//------------------------------------------------------------------------------
void
Draw::Render(const void* pixels, int width, int height) {
    o_trace_scoped(yakc_draw);

    // create new texture is size mismatch
    this->validateTexture(width, height);
    if (!this->texture.IsValid()) {
        // width or height 0 (can happen if emulator is switched off)
        return;
    }
    this->crtDrawState.FSTexture[YAKCTextures::IRM] = this->texture;
    this->nocrtDrawState.FSTexture[YAKCTextures::IRM] = this->texture;
    this->texUpdateAttrs.Sizes[0][0] = width*height*4;
    Gfx::UpdateTexture(this->texture, pixels, this->texUpdateAttrs);
    this->applyViewport(width, height);
    if (this->crtEffectEnabled) {
        Oryol::CRTShader::YAKCFSParams fsParams;
        fsParams.ColorTV = this->crtColorEnabled;
        fsParams.CRTWarp = this->crtWarp;
        Gfx::ApplyDrawState(this->crtDrawState);
        Gfx::ApplyUniformBlock(fsParams);
    }
    else {
        Oryol::NoCRTShader::YAKCFSParams fsParams;
        fsParams.ColorTV = this->crtColorEnabled;
        fsParams.CRTWarp = this->crtWarp;
        Gfx::ApplyDrawState(this->nocrtDrawState);
        Gfx::ApplyUniformBlock(fsParams);
    }
    Gfx::Draw();
    this->restoreViewport();
}

//------------------------------------------------------------------------------
void
Draw::applyViewport(int width, int height) {
    float aspect = float(width) / float(height);
    if (aspect > (5.0f/4.0f)) {
        aspect = (5.0f/4.0f);
    }
    const int fbWidth = Gfx::DisplayAttrs().FramebufferWidth;
    const int fbHeight = Gfx::DisplayAttrs().FramebufferHeight;
    int viewPortY = this->frameSizeY;
    int viewPortH = fbHeight - 2*frameSizeY;
    int viewPortW = (const int) (fbHeight * aspect) - 2*this->frameSizeX;
    int viewPortX = (fbWidth - viewPortW) / 2;
    Gfx::ApplyViewPort(viewPortX, viewPortY, viewPortW, viewPortH);
}

//------------------------------------------------------------------------------
void
Draw::restoreViewport() {
    const int fbWidth = Gfx::DisplayAttrs().FramebufferWidth;
    const int fbHeight = Gfx::DisplayAttrs().FramebufferHeight;
    Gfx::ApplyViewPort(0, 0, fbWidth, fbHeight);
}

//------------------------------------------------------------------------------
void
Draw::validateTexture(int width, int height) {
    if ((this->texWidth != width) || (this->texHeight != height)) {

        this->texWidth = width;
        this->texHeight = height;

        // if a texture exists, discard it
        if (this->texture.IsValid()) {
            Gfx::DestroyResources('YAKC');
            this->texture.Invalidate();
        }

        // only create a new texture if width and height > 0
        if ((width > 0) && (height > 0)) {
            auto texSetup = TextureSetup::Empty(width, height, 1, TextureType::Texture2D, PixelFormat::RGBA8, Usage::Stream);
            texSetup.Sampler.MinFilter = TextureFilterMode::Linear;
            texSetup.Sampler.MagFilter = TextureFilterMode::Linear  ;
            texSetup.Sampler.WrapU = TextureWrapMode::ClampToEdge;
            texSetup.Sampler.WrapV = TextureWrapMode::ClampToEdge;
            Gfx::PushResourceLabel('YAKC');
            this->texture = Gfx::CreateResource(texSetup);
            Gfx::PopResourceLabel();

        }
    }
}

} // namespace YAKC
<commit_msg>Fixes for upcoming webgl2 merge<commit_after>//------------------------------------------------------------------------------
//  Draw.cc
//------------------------------------------------------------------------------
#include "Draw.h"
#include "yakc_shaders.h"

using namespace Oryol;

namespace YAKC {

//------------------------------------------------------------------------------
void
Draw::Setup(const GfxSetup& gfxSetup, int frame_x, int frame_y) {
    this->crtEffectEnabled = false;
    this->crtColorEnabled = true;
    this->frameSizeX = frame_x;
    this->frameSizeY = frame_y;
    this->texUpdateAttrs.NumFaces = 1;
    this->texUpdateAttrs.NumMipMaps = 1;
    this->texUpdateAttrs.Sizes[0][0] = 0;

    auto fsqSetup = MeshSetup::FullScreenQuad(true);
    Id fsq = Gfx::CreateResource(fsqSetup);
    this->crtDrawState.Mesh[0] = fsq;
    this->nocrtDrawState.Mesh[0] = fsq;

    Id crtShd = Gfx::CreateResource(CRTShader::Setup());
    Id nocrtShd = Gfx::CreateResource(NoCRTShader::Setup());
    auto pips = PipelineSetup::FromLayoutAndShader(fsqSetup.Layout, crtShd);
    pips.DepthStencilState.DepthWriteEnabled = false;
    pips.DepthStencilState.DepthCmpFunc = CompareFunc::Always;
    pips.BlendState.ColorFormat = gfxSetup.ColorFormat;
    pips.BlendState.DepthFormat = gfxSetup.DepthFormat;
    pips.RasterizerState.SampleCount = gfxSetup.SampleCount;
    this->crtDrawState.Pipeline = Gfx::CreateResource(pips);
    pips.Shader = nocrtShd;
    this->nocrtDrawState.Pipeline = Gfx::CreateResource(pips);
}

//------------------------------------------------------------------------------
void
Draw::Discard() {
    // nothing to do here
}

//------------------------------------------------------------------------------
void
Draw::UpdateParams(bool enableCrtEffect, bool colorTV, const glm::vec2& warp) {
    this->crtEffectEnabled = enableCrtEffect;
    this->crtColorEnabled = colorTV;
    this->crtWarp = warp;
}

//------------------------------------------------------------------------------
void
Draw::Render(const void* pixels, int width, int height) {

    // create new texture is size mismatch
    this->validateTexture(width, height);
    if (!this->texture.IsValid()) {
        // width or height 0 (can happen if emulator is switched off)
        return;
    }
    this->crtDrawState.FSTexture[YAKCTextures::IRM] = this->texture;
    this->nocrtDrawState.FSTexture[YAKCTextures::IRM] = this->texture;
    this->texUpdateAttrs.Sizes[0][0] = width*height*4;
    Gfx::UpdateTexture(this->texture, pixels, this->texUpdateAttrs);
    this->applyViewport(width, height);
    if (this->crtEffectEnabled) {
        Oryol::CRTShader::YAKCFSParams fsParams;
        fsParams.ColorTV = this->crtColorEnabled;
        fsParams.CRTWarp = this->crtWarp;
        Gfx::ApplyDrawState(this->crtDrawState);
        Gfx::ApplyUniformBlock(fsParams);
    }
    else {
        Oryol::NoCRTShader::YAKCFSParams fsParams;
        fsParams.ColorTV = this->crtColorEnabled;
        fsParams.CRTWarp = this->crtWarp;
        Gfx::ApplyDrawState(this->nocrtDrawState);
        Gfx::ApplyUniformBlock(fsParams);
    }
    Gfx::Draw();
    this->restoreViewport();
}

//------------------------------------------------------------------------------
void
Draw::applyViewport(int width, int height) {
    float aspect = float(width) / float(height);
    if (aspect > (5.0f/4.0f)) {
        aspect = (5.0f/4.0f);
    }
    const int fbWidth = Gfx::DisplayAttrs().FramebufferWidth;
    const int fbHeight = Gfx::DisplayAttrs().FramebufferHeight;
    int viewPortY = this->frameSizeY;
    int viewPortH = fbHeight - 2*frameSizeY;
    int viewPortW = (const int) (fbHeight * aspect) - 2*this->frameSizeX;
    int viewPortX = (fbWidth - viewPortW) / 2;
    Gfx::ApplyViewPort(viewPortX, viewPortY, viewPortW, viewPortH);
}

//------------------------------------------------------------------------------
void
Draw::restoreViewport() {
    const int fbWidth = Gfx::DisplayAttrs().FramebufferWidth;
    const int fbHeight = Gfx::DisplayAttrs().FramebufferHeight;
    Gfx::ApplyViewPort(0, 0, fbWidth, fbHeight);
}

//------------------------------------------------------------------------------
void
Draw::validateTexture(int width, int height) {
    if ((this->texWidth != width) || (this->texHeight != height)) {

        this->texWidth = width;
        this->texHeight = height;

        // if a texture exists, discard it
        if (this->texture.IsValid()) {
            Gfx::DestroyResources('YAKC');
            this->texture.Invalidate();
        }

        // only create a new texture if width and height > 0
        if ((width > 0) && (height > 0)) {
            auto texSetup = TextureSetup::Empty2D(width, height, 1, PixelFormat::RGBA8, Usage::Stream);
            texSetup.Sampler.MinFilter = TextureFilterMode::Linear;
            texSetup.Sampler.MagFilter = TextureFilterMode::Linear  ;
            texSetup.Sampler.WrapU = TextureWrapMode::ClampToEdge;
            texSetup.Sampler.WrapV = TextureWrapMode::ClampToEdge;
            Gfx::PushResourceLabel('YAKC');
            this->texture = Gfx::CreateResource(texSetup);
            Gfx::PopResourceLabel();

        }
    }
}

} // namespace YAKC
<|endoftext|>


<commit_before>/*
 * cclive Copyright (C) 2009 Toni Gundogdu. This file is part of cclive.
 *
 * cclive is free software: you can redistribute it and/or modify it under the
 * terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 *
 * cclive 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 General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <iostream>
#include <iomanip>
#include <sstream>
#include <string>
#include <cstdlib>
#include <cstring>
#include <curl/curl.h>

#include "singleton.h"
#include "cmdline.h"
#include "opts.h"
#include "except.h"
#include "macros.h"
#include "video.h"
#include "progressbar.h"
#include "macros.h"
#include "curl.h"

static CURL *curl;
static char curlErrorBuffer[CURL_ERROR_SIZE];

CurlMgr::CurlMgr()
{
    curl = NULL;
    curlErrorBuffer[0] = '\0';
}

    // Keeps -Weffc++ happy.
CurlMgr::CurlMgr(const CurlMgr&)
{
    curl = NULL;
    curlErrorBuffer[0] = '\0';
}

    // Ditto.
CurlMgr&
CurlMgr::operator=(const CurlMgr&) {
    return *this;
}

CurlMgr::~CurlMgr() {
    curl_easy_cleanup(curl);
    curl = NULL;
}

void
CurlMgr::init() {
    curl = curl_easy_init();
    if (!curl)
        throw RuntimeException("error: curl_easy_init returned NULL\n");

    Options opts = optsmgr.getOptions();

    curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
    curl_easy_setopt(curl, CURLOPT_USERAGENT, opts.agent_arg);
    curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
    curl_easy_setopt(curl, CURLOPT_AUTOREFERER, 1L);
    curl_easy_setopt(curl, CURLOPT_NOBODY, 0L);
    curl_easy_setopt(curl, CURLOPT_VERBOSE, opts.debug_given);
    curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curlErrorBuffer);

    char *proxy = opts.proxy_arg;
    if (opts.no_proxy_given)
        proxy = const_cast<char*>("");

    curl_easy_setopt(curl, CURLOPT_PROXY, proxy);
}

struct mem_s {
    mem_s() : content("") { }
    std::string content;
};

static size_t
callback_writemem(void *p, size_t size, size_t nmemb, void *data) {
    mem_s *m    = reinterpret_cast<mem_s *>(data);
    m->content += reinterpret_cast<char*>(p);
    return size * nmemb;
}

std::string
CurlMgr::fetchToMem(const std::string& url, const std::string &what) {
    std::cout << "fetch ";
    if (what.empty())
        std::cout << url;
    else
        std::cout << what;
    std::cout << " ..." << std::flush;

    curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
    curl_easy_setopt(curl, CURLOPT_ENCODING, "");

    mem_s mem;
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &mem);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, callback_writemem);

    Options opts = optsmgr.getOptions();

    curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT,
        opts.connect_timeout_arg);

    /*
     * http://curl.haxx.se/docs/knownbugs.html:
     *
     * "When connecting to a SOCKS proxy, the (connect) timeout is
     * not properly acknowledged after the actual TCP connect (during
     * the SOCKS 'negotiate' phase)."
     */
    curl_easy_setopt(curl, CURLOPT_TIMEOUT,
        opts.connect_timeout_socks_arg);

    curlErrorBuffer[0] = '\0';
    CURLcode rc = curl_easy_perform(curl);

    curl_easy_setopt(curl, CURLOPT_TIMEOUT, 0L);

    std::string errmsg;

    if (CURLE_OK == rc) {
        long httpcode = 0;
        curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &httpcode);
        if (httpcode == 200)
            std::cout << "done." << std::endl;
        else
            errmsg = "server returned http/"+httpcode;
    }
    else
        errmsg = curlErrorBuffer;

    curlErrorBuffer[0] = '\0';

    if (!errmsg.empty())
        throw FetchException(errmsg);

    return mem.content;
}

void
CurlMgr::queryFileLength(VideoProperties& props) {
    FILE *f = tmpfile();
    if (!f) {
        perror("tmpfile");
        throw RuntimeException("error: tmpfile(3) failed");
    }
    std::cout << "verify video link ..." << std::flush;

    Options opts = optsmgr.getOptions();

    curl_easy_setopt(curl, CURLOPT_URL, props.getLink().c_str());
    curl_easy_setopt(curl, CURLOPT_NOBODY, 1L);      // GET -> HEAD
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, f);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, NULL);
    curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT,
                     opts.connect_timeout_arg);
    curl_easy_setopt(curl, CURLOPT_TIMEOUT,
                     opts.connect_timeout_socks_arg);

    curlErrorBuffer[0] = '\0';
    CURLcode rc = curl_easy_perform(curl);

    curl_easy_setopt(curl, CURLOPT_TIMEOUT, 0L);
    curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); // reset HEAD -> GET

    fflush(f);
    fclose(f);

    long httpcode = 0;
    curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &httpcode);

    double len = 0;
    char *ct = NULL;

    std::string errmsg;

    if (rc == CURLE_OK && httpcode == 200) {
        rc = curl_easy_getinfo(curl, CURLINFO_CONTENT_TYPE, &ct);

        if (CURLE_OK == rc && NULL != ct)
            curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &len);

        if (CURLE_OK == rc && NULL != ct && len > 0) {
            props.setLength(len);
            props.setContentType(ct);
            std::cout << "done." << std::endl;
        }
        else {
            curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &httpcode);
            errmsg = "server returned http/"+httpcode;
        }
    }
    else {
        if (strlen(curlErrorBuffer) > 0)
            errmsg = curlErrorBuffer;
        else
            errmsg = "server returned http/"+httpcode;
    }

    curlErrorBuffer[0] = '\0';

    if (!errmsg.empty())
        throw FetchException(errmsg);
}

struct write_s {
    write_s()
        : filename(NULL), initial(0), file(NULL) { }
    char *filename;
    double initial;
    FILE *file;
};

static size_t
callback_writefile(void *data, size_t size, size_t nmemb, void *p) {
    write_s *write = reinterpret_cast<write_s*>(p);
    if (NULL != write && !write->file) {
        const char *mode = write->initial ? "ab" : "wb";
        write->file = fopen(write->filename, mode);
        if (!write->file) {
            perror("fopen");
            return -1;
        }
    }
    return fwrite(data, size, nmemb, write->file);
}

int
callback_progress(
    void *p,
    double total,
    double now,
    double utotal,
    double unow)
{
    ProgressBar *pb = static_cast<ProgressBar*>(p);
    pb->update(now);
    return 0;
}

void
CurlMgr::fetchToFile(VideoProperties& props) {
    Options opts = optsmgr.getOptions();

    double initial = props.getInitial();

    if (opts.continue_given && initial > 0) {
        double remaining = props.getLength() - initial;
        std::cout
            << "from: "
            << std::setprecision(0)
            << initial
            << " ("
            << std::setprecision(1)
            << _TOMB(initial)
            << "M)  remaining: "
            << std::setprecision(0)
            << remaining
            << " ("
            << std::setprecision(1)
            << _TOMB(remaining)
            << "M)"
            << std::endl;
    }

    curl_easy_setopt(curl, CURLOPT_URL, props.getLink().c_str());
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, callback_writefile);

    write_s write;
    memset(&write, 0, sizeof(write));

    write.initial  = initial;
    write.filename = const_cast<char*>(props.getFilename().c_str());

    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &write);

    curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
    curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, &callback_progress);

    ProgressBar pb;
    pb.init(props);
    curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, &pb);

    curl_easy_setopt(curl, CURLOPT_ENCODING, "identity");
    curl_easy_setopt(curl, CURLOPT_HEADER, 0L);
    curl_easy_setopt(curl, CURLOPT_RESUME_FROM, (long)initial);
    curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT,
                     opts.connect_timeout_arg);

    curl_off_t limit_rate = opts.limit_rate_arg * 1024;
    curl_easy_setopt(curl, CURLOPT_MAX_RECV_SPEED_LARGE, limit_rate);

    curlErrorBuffer[0] = '\0';
    CURLcode rc = curl_easy_perform(curl);

    curl_easy_setopt(curl, CURLOPT_HEADER, 1L);
    curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L);
    curl_easy_setopt(curl, CURLOPT_RESUME_FROM, 0L);
    curl_easy_setopt(curl, CURLOPT_MAX_RECV_SPEED_LARGE, (curl_off_t) 0);

    std::string errmsg;

    if (CURLE_OK != rc) {
        long httpcode = 0;
        curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &httpcode);

        if (strlen(curlErrorBuffer) > 0)
            errmsg = curlErrorBuffer;
        else
            errmsg = "server closed with http/"+httpcode;
    }

    curlErrorBuffer[0] = '\0';

    if (NULL != write.file) {
        fflush(write.file);
        fclose(write.file);
    }

    if (!errmsg.empty())
        throw FetchException(errmsg);

    pb.finish();

    std::cout << std::endl;
}

const std::string&
CurlMgr::unescape(std::string& url) const {
    char *p = curl_easy_unescape(curl, url.c_str(), 0, 0);
    url     = p;
    _FREE(p);
    return url;
}

void
CurlMgr::logIntoYoutube() {
    Options opts = optsmgr.getOptions();

    std::stringstream b;
    std::string passw;

    if (!opts.youtube_pass_given) {
        b   << "Login password for "
            << opts.youtube_user_arg
            << ":"
            << std::flush;
        passw = getpass(b.str().c_str());
    }
    else
        passw = opts.youtube_pass_arg;

    std::stringstream req;
    req << "http://uk.youtube.com/login?current_form=loginform"
        << "&username=" << opts.youtube_user_arg
        << "&password=" << passw
        << "&action_login=log+in&hl=en-GB";

    passw.clear();

    curl_easy_setopt(curl, CURLOPT_URL, req.str().c_str());
    curl_easy_setopt(curl, CURLOPT_COOKIEFILE, "");
    curl_easy_setopt(curl, CURLOPT_ENCODING, "");
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, callback_writemem);

    mem_s mem;
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &mem);

    curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT,
                     opts.connect_timeout_arg);
    curl_easy_setopt(curl, CURLOPT_TIMEOUT,
                     opts.connect_timeout_socks_arg);

    std::cout
        << "[youtube] login as "
        << opts.youtube_user_arg
        << "..."
        << std::flush;

    CURLcode rc = curl_easy_perform(curl);

    curl_easy_setopt(curl, CURLOPT_TIMEOUT, 0L);

    if (CURLE_OK == rc) {
        long    httpcode;
        curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &httpcode);
        if (httpcode == 200) {
            if (mem.content.find(
                "your log-in was incorrect") != std::string::npos)
            {
                throw RuntimeException("login was incorrect");
            }
            else if(mem.content.find(
                "check your password") != std::string::npos)
            {
                throw RuntimeException("check your login password");
            }
            else if (mem.content.find(
                "too many login failures") != std::string::npos)
            {
                throw RuntimeException("too many login failures, try later");
            }
            else {
                curl_easy_setopt(curl, CURLOPT_COOKIE, "is_adult=1");
                std::cout << "done." << std::endl;
            }
        }
        else
            throw RuntimeException("server returned http/"+httpcode);
    }
}

CurlMgr::FetchException::FetchException(const std::string& error)
    : RuntimeException(error)
{
}
<commit_msg>Add HAVE_GETPASS check.<commit_after>/*
 * cclive Copyright (C) 2009 Toni Gundogdu. This file is part of cclive.
 *
 * cclive is free software: you can redistribute it and/or modify it under the
 * terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 *
 * cclive 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 General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "config.h"

#include <iostream>
#include <iomanip>
#include <sstream>
#include <string>
#include <cstdlib>
#include <cstring>
#include <curl/curl.h>

#include "singleton.h"
#include "cmdline.h"
#include "opts.h"
#include "except.h"
#include "macros.h"
#include "video.h"
#include "progressbar.h"
#include "macros.h"
#include "curl.h"

static CURL *curl;
static char curlErrorBuffer[CURL_ERROR_SIZE];

CurlMgr::CurlMgr()
{
    curl = NULL;
    curlErrorBuffer[0] = '\0';
}

    // Keeps -Weffc++ happy.
CurlMgr::CurlMgr(const CurlMgr&)
{
    curl = NULL;
    curlErrorBuffer[0] = '\0';
}

    // Ditto.
CurlMgr&
CurlMgr::operator=(const CurlMgr&) {
    return *this;
}

CurlMgr::~CurlMgr() {
    curl_easy_cleanup(curl);
    curl = NULL;
}

void
CurlMgr::init() {
    curl = curl_easy_init();
    if (!curl)
        throw RuntimeException("error: curl_easy_init returned NULL\n");

    Options opts = optsmgr.getOptions();

    curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
    curl_easy_setopt(curl, CURLOPT_USERAGENT, opts.agent_arg);
    curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
    curl_easy_setopt(curl, CURLOPT_AUTOREFERER, 1L);
    curl_easy_setopt(curl, CURLOPT_NOBODY, 0L);
    curl_easy_setopt(curl, CURLOPT_VERBOSE, opts.debug_given);
    curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curlErrorBuffer);

    char *proxy = opts.proxy_arg;
    if (opts.no_proxy_given)
        proxy = const_cast<char*>("");

    curl_easy_setopt(curl, CURLOPT_PROXY, proxy);
}

struct mem_s {
    mem_s() : content("") { }
    std::string content;
};

static size_t
callback_writemem(void *p, size_t size, size_t nmemb, void *data) {
    mem_s *m    = reinterpret_cast<mem_s *>(data);
    m->content += reinterpret_cast<char*>(p);
    return size * nmemb;
}

std::string
CurlMgr::fetchToMem(const std::string& url, const std::string &what) {
    std::cout << "fetch ";
    if (what.empty())
        std::cout << url;
    else
        std::cout << what;
    std::cout << " ..." << std::flush;

    curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
    curl_easy_setopt(curl, CURLOPT_ENCODING, "");

    mem_s mem;
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &mem);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, callback_writemem);

    Options opts = optsmgr.getOptions();

    curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT,
        opts.connect_timeout_arg);

    /*
     * http://curl.haxx.se/docs/knownbugs.html:
     *
     * "When connecting to a SOCKS proxy, the (connect) timeout is
     * not properly acknowledged after the actual TCP connect (during
     * the SOCKS 'negotiate' phase)."
     */
    curl_easy_setopt(curl, CURLOPT_TIMEOUT,
        opts.connect_timeout_socks_arg);

    curlErrorBuffer[0] = '\0';
    CURLcode rc = curl_easy_perform(curl);

    curl_easy_setopt(curl, CURLOPT_TIMEOUT, 0L);

    std::string errmsg;

    if (CURLE_OK == rc) {
        long httpcode = 0;
        curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &httpcode);
        if (httpcode == 200)
            std::cout << "done." << std::endl;
        else
            errmsg = "server returned http/"+httpcode;
    }
    else
        errmsg = curlErrorBuffer;

    curlErrorBuffer[0] = '\0';

    if (!errmsg.empty())
        throw FetchException(errmsg);

    return mem.content;
}

void
CurlMgr::queryFileLength(VideoProperties& props) {
    FILE *f = tmpfile();
    if (!f) {
        perror("tmpfile");
        throw RuntimeException("error: tmpfile(3) failed");
    }
    std::cout << "verify video link ..." << std::flush;

    Options opts = optsmgr.getOptions();

    curl_easy_setopt(curl, CURLOPT_URL, props.getLink().c_str());
    curl_easy_setopt(curl, CURLOPT_NOBODY, 1L);      // GET -> HEAD
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, f);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, NULL);
    curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT,
                     opts.connect_timeout_arg);
    curl_easy_setopt(curl, CURLOPT_TIMEOUT,
                     opts.connect_timeout_socks_arg);

    curlErrorBuffer[0] = '\0';
    CURLcode rc = curl_easy_perform(curl);

    curl_easy_setopt(curl, CURLOPT_TIMEOUT, 0L);
    curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); // reset HEAD -> GET

    fflush(f);
    fclose(f);

    long httpcode = 0;
    curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &httpcode);

    double len = 0;
    char *ct = NULL;

    std::string errmsg;

    if (rc == CURLE_OK && httpcode == 200) {
        rc = curl_easy_getinfo(curl, CURLINFO_CONTENT_TYPE, &ct);

        if (CURLE_OK == rc && NULL != ct)
            curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &len);

        if (CURLE_OK == rc && NULL != ct && len > 0) {
            props.setLength(len);
            props.setContentType(ct);
            std::cout << "done." << std::endl;
        }
        else {
            curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &httpcode);
            errmsg = "server returned http/"+httpcode;
        }
    }
    else {
        if (strlen(curlErrorBuffer) > 0)
            errmsg = curlErrorBuffer;
        else
            errmsg = "server returned http/"+httpcode;
    }

    curlErrorBuffer[0] = '\0';

    if (!errmsg.empty())
        throw FetchException(errmsg);
}

struct write_s {
    write_s()
        : filename(NULL), initial(0), file(NULL) { }
    char *filename;
    double initial;
    FILE *file;
};

static size_t
callback_writefile(void *data, size_t size, size_t nmemb, void *p) {
    write_s *write = reinterpret_cast<write_s*>(p);
    if (NULL != write && !write->file) {
        const char *mode = write->initial ? "ab" : "wb";
        write->file = fopen(write->filename, mode);
        if (!write->file) {
            perror("fopen");
            return -1;
        }
    }
    return fwrite(data, size, nmemb, write->file);
}

int
callback_progress(
    void *p,
    double total,
    double now,
    double utotal,
    double unow)
{
    ProgressBar *pb = static_cast<ProgressBar*>(p);
    pb->update(now);
    return 0;
}

void
CurlMgr::fetchToFile(VideoProperties& props) {
    Options opts = optsmgr.getOptions();

    double initial = props.getInitial();

    if (opts.continue_given && initial > 0) {
        double remaining = props.getLength() - initial;
        std::cout
            << "from: "
            << std::setprecision(0)
            << initial
            << " ("
            << std::setprecision(1)
            << _TOMB(initial)
            << "M)  remaining: "
            << std::setprecision(0)
            << remaining
            << " ("
            << std::setprecision(1)
            << _TOMB(remaining)
            << "M)"
            << std::endl;
    }

    curl_easy_setopt(curl, CURLOPT_URL, props.getLink().c_str());
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, callback_writefile);

    write_s write;
    memset(&write, 0, sizeof(write));

    write.initial  = initial;
    write.filename = const_cast<char*>(props.getFilename().c_str());

    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &write);

    curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
    curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, &callback_progress);

    ProgressBar pb;
    pb.init(props);
    curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, &pb);

    curl_easy_setopt(curl, CURLOPT_ENCODING, "identity");
    curl_easy_setopt(curl, CURLOPT_HEADER, 0L);
    curl_easy_setopt(curl, CURLOPT_RESUME_FROM, (long)initial);
    curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT,
                     opts.connect_timeout_arg);

    curl_off_t limit_rate = opts.limit_rate_arg * 1024;
    curl_easy_setopt(curl, CURLOPT_MAX_RECV_SPEED_LARGE, limit_rate);

    curlErrorBuffer[0] = '\0';
    CURLcode rc = curl_easy_perform(curl);

    curl_easy_setopt(curl, CURLOPT_HEADER, 1L);
    curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L);
    curl_easy_setopt(curl, CURLOPT_RESUME_FROM, 0L);
    curl_easy_setopt(curl, CURLOPT_MAX_RECV_SPEED_LARGE, (curl_off_t) 0);

    std::string errmsg;

    if (CURLE_OK != rc) {
        long httpcode = 0;
        curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &httpcode);

        if (strlen(curlErrorBuffer) > 0)
            errmsg = curlErrorBuffer;
        else
            errmsg = "server closed with http/"+httpcode;
    }

    curlErrorBuffer[0] = '\0';

    if (NULL != write.file) {
        fflush(write.file);
        fclose(write.file);
    }

    if (!errmsg.empty())
        throw FetchException(errmsg);

    pb.finish();

    std::cout << std::endl;
}

const std::string&
CurlMgr::unescape(std::string& url) const {
    char *p = curl_easy_unescape(curl, url.c_str(), 0, 0);
    url     = p;
    _FREE(p);
    return url;
}

void
CurlMgr::logIntoYoutube() {
#ifdef HAVE_GETPASS
    Options opts = optsmgr.getOptions();

    std::stringstream b;
    std::string passw;

    if (!opts.youtube_pass_given) {
        b   << "Login password for "
            << opts.youtube_user_arg
            << ":"
            << std::flush;
        passw = getpass(b.str().c_str());
    }
    else
        passw = opts.youtube_pass_arg;

    std::stringstream req;
    req << "http://uk.youtube.com/login?current_form=loginform"
        << "&username=" << opts.youtube_user_arg
        << "&password=" << passw
        << "&action_login=log+in&hl=en-GB";

    passw.clear();

    curl_easy_setopt(curl, CURLOPT_URL, req.str().c_str());
    curl_easy_setopt(curl, CURLOPT_COOKIEFILE, "");
    curl_easy_setopt(curl, CURLOPT_ENCODING, "");
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, callback_writemem);

    mem_s mem;
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &mem);

    curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT,
                     opts.connect_timeout_arg);
    curl_easy_setopt(curl, CURLOPT_TIMEOUT,
                     opts.connect_timeout_socks_arg);

    std::cout
        << "[youtube] login as "
        << opts.youtube_user_arg
        << "..."
        << std::flush;

    CURLcode rc = curl_easy_perform(curl);

    curl_easy_setopt(curl, CURLOPT_TIMEOUT, 0L);

    if (CURLE_OK == rc) {
        long    httpcode;
        curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &httpcode);
        if (httpcode == 200) {
            if (mem.content.find(
                "your log-in was incorrect") != std::string::npos)
            {
                throw RuntimeException("login was incorrect");
            }
            else if(mem.content.find(
                "check your password") != std::string::npos)
            {
                throw RuntimeException("check your login password");
            }
            else if (mem.content.find(
                "too many login failures") != std::string::npos)
            {
                throw RuntimeException("too many login failures, try later");
            }
            else {
                curl_easy_setopt(curl, CURLOPT_COOKIE, "is_adult=1");
                std::cout << "done." << std::endl;
            }
        }
        else
            throw RuntimeException("server returned http/"+httpcode);
    }
#else
    throw RuntimeException("getpass: no support");
#endif // HAVE_GETPASS
}

CurlMgr::FetchException::FetchException(const std::string& error)
    : RuntimeException(error)
{
}
<|endoftext|>


<commit_before>// Marlo Zeroth mzero001@ucr.edu 861309346
// Emmilio Segovia esego001@ucr.edu 861305177

#include "command.h"

// Main constructor

Command::Command(std::vector<std::string> input) {

    // We only process if the input is valid
    if (input.empty()) {
        throw std::invalid_argument("Invalid argument. Empty vector parameter.");
    }
    else {
        // We prepare an array of cstrings to pass to execvp
        // Plus an extra for the null terminated string
        cmd = new char* [input.size() + 1];
        size = input.size();

        // Iterate through the vector and copy the strings
        for (unsigned int i = 0; i < input.size(); i++) {
            cmd[i] = new char [input.at(i).size() + 1];
            std::strncpy(cmd[i], input.at(i).c_str(), input.at(i).size() + 1);
        }
        cmd[input.size()] = NULL;
    }
}

// Destructor

Command::~Command() {
    if (cmd) {
        // Delete the children first
        //for (int i = 0; cmd[i] != NULL; i++) {
        //delete cmd[i];
        //}
        // Now deallocate the parent
        delete [] cmd;
    }
}

// We are a leaf. We have no children.

Base * Command::get_left() {
    return NULL;
}

// We are a leaf. We have no children.

Base * Command::get_right() {
    return NULL;
}

// Since we have no children we do nothing

void Command::set_left(Base* left) {

}

// Since we have no children we do nothing

void Command::set_right(Base* right) {

}

// Always returns true. We are a leaf.

bool Command::isLeaf() {
    return true;
}

// Returns the contents of the command. If there are no
// commands returns an empty string

std::string Command::to_string() {
    std::string temp;
    if (cmd) {
        for (unsigned int i = 0; cmd[i] != NULL; i++) {
            temp += std::string(cmd[i]) + " ";
        }
    }
    return temp;
}

// Returns a vector of the contents. If there are no commands
// returns an empty vector.

std::vector<std::string> Command::to_vector() {
    std::vector<std::string> v;
    if (cmd) {

        for (unsigned int i = 0; cmd[i] != NULL; i++) {
            std::string temp(std::string(cmd[i]));
            v.push_back(temp);
        }
    }
    return v;
}
// We print out our data.

void Command::print() {
    if (cmd) {
        for (unsigned int i = 0; cmd[i] != NULL; i++) {
            std::cout << cmd[i] << " ";
        }
        std::cout << std::endl;
    }
}

// We execute

int Command::execute() {
    int returnValue;

    // Find which command to execute
    if (!std::strcmp(cmd[0], "exit")) {
        returnValue = exit_command();
    } else if (!std::strcmp(cmd[0], "test") || !std::strcmp(cmd[0], "[")) {
        returnValue = test_command();
    } else if (!std::strcmp(cmd[0], "cd")) {
        returnValue = cd_command();
    } else {
        returnValue = system_call();
    }

    // Return its value
    return returnValue;
}

// We issue the exit command

int Command::exit_command() {
    // We return -1 to signify exit.
    return -1;
}

// We issue the system call

int Command::system_call() {
    pid_t cpid, w; // pid of child and pid of process that has changed

    // Assuming the call to execv succeeds we set status = 0
    // and handle the result when call to execvp is made
    int status;

    int result; // final result after running execvp

    // Now we are almost ready to execute
    int exec_pipe[2]; // For communicating using a pipe

    // Create the pipe.
    if (pipe(exec_pipe)) {
        perror("pipe failed");
        return EXIT_FAILURE;
    }

    // We fork here and split into parent and child processes.
    if ((cpid = fork()) < 0) {
        perror("process failed");
        exit(EXIT_FAILURE);
    }// We are in the child process
    else if (cpid == 0) {

        // We close the read end of the pipe in the child
        close(exec_pipe[0]);

        // We call the fcntl function so that the child can set the FD_CLOEXEC flag
        // when exevp runs
        if (fcntl(exec_pipe[1], F_SETFD, fcntl(exec_pipe[1], F_GETFD) | FD_CLOEXEC)) {
            perror("fcntl failed");
            return EX_OSERR;
        }

        // Now we execute the command
        if ((status = execvp(cmd[0], cmd)) == -1) {
            // Now we write the error number result to the pipe
            write(exec_pipe[1], &errno, sizeof (int));
            std::string temp(cmd[0]);
            std::string s(": command not found");
            temp += s;
            perror(temp.c_str());
        }

    } else {
        // We are in the parent process now
        // We make parent process wait for child processes to exit
        do {

            // We close the writing end of the pipe in parent
            close(exec_pipe[1]);

            //If there was an error during wait we exit

            if ((w = waitpid(cpid, &status, WUNTRACED)) == -1) {
                perror("waitpid");
                exit(EXIT_FAILURE);
            }

        } while (!WIFEXITED(status) && !WIFSIGNALED(status));
    }

    // Return the error number the child process stored in the pipe after execvp.
    // If successfull it should return 0 or if it failed return the error number.
    result = read(exec_pipe[0], &errno, sizeof (errno));

    return result;
}

// We issue the test command

int Command::test_command() {
    //size_t size = PATH_MAX + 1;
    int status; // Return value after execution
    //char *buffer1 = new char[PATH_MAX + 1]; /* not sure about the "+ 1" */
    //char *buffer2 = new char[PATH_MAX+1]; /* not sure about the "+ 1" */
    //std::string temp("../");
    //temp += std::string(cmd[2]);

    //char *resolvedPath = realpath(cmd[2], buffer1);

    //char *currentDir = getcwd(buffer2, size);
    //if (resolvedPath) {

   // } else {
        //perror("Error");
    //}

    // We test whether we are dealing with
    struct stat sb;

    // We check how the test command was passed. Either it there were flags or no flags.
    if (!std::strcmp(cmd[1], "-e") || !std::strcmp(cmd[1], "-d")
            || !std::strcmp(cmd[1], "-f")) {
        // We test whether this is a file, directory or otherwise
        if (stat(cmd[2], &sb) == -1) {
            status = 1;
            //perror("stat"); // Outputs an error to the command line
        } else {
            // If it is either a directory or file
            if ((!std::strcmp(cmd[1], "-e")) && ((S_ISREG(sb.st_mode) == 1)
                    || (S_ISDIR(sb.st_mode) == 1))) {
                status = 0;
            }                // If it is a file only
            else if (!std::strcmp(cmd[1], "-f") && S_ISREG(sb.st_mode) == 1) {
                status = 0;
            }// If it is a directory only
            else if (!std::strcmp(cmd[1], "-d") && S_ISDIR(sb.st_mode) == 1) {
                status = 0;
            } else {
                status = 1;
            }
        }
    } else {
        // We test whether this is a file, directory or otherwise
        if (stat(cmd[1], &sb) == -1) {
            status = 1;
            //perror("stat"); // Outputs an error to the command line
        } else {
            // If it is either a directory or file we return true
            if (((S_ISREG(sb.st_mode) == 1) || (S_ISDIR(sb.st_mode) == 1))) {
                status = 0;
            } else {
                status = 1;
            }
        }
    }

    //delete buffer1;
    //delete buffer2;
    // We output the success of the test function
    if (status == 0) {
        std::cout << "(True)" << std::endl;
    } else {
        std::cout << "(False)" << std::endl;
    }
    return status;
}

int Command::cd_command() {
    char buffer[MAXPATHLEN];        //buffer for getcwd command
    const char *curPath = getcwd(buffer, MAXPATHLEN);       //get the current path
    const char *newPath;            //the new path

    //determine new path
    if (size == 1) {
        newPath = getenv("HOME");       //get the home path
    }
    else if (!std::strcmp(cmd[1], "-")) {
        newPath = getenv("OLDPWD");
    }
    else {
        newPath = cmd[1];
    }

    //perform data movement with error checking
    if (setenv("OLDPWD", curPath, 1) == -1) {   //set OLDPWD to current path
        perror("Could not update OLDPWD variable.");
        return 1;
    }
    if (setenv("PWD", newPath, 1) == -1) {  //set PWD to HOME path
        perror("Could not update PWD variable.");
        return 1;
    }
    if (chdir(newPath) == -1) {     //update current directory
        perror("Could not change directory.");
        return 1;
    }
        return 0;
}

<commit_msg>Fixed ~/path readability in command.cpp<commit_after>// Marlo Zeroth mzero001@ucr.edu 861309346
// Emmilio Segovia esego001@ucr.edu 861305177

#include "command.h"

// Main constructor

Command::Command(std::vector<std::string> input) {

    // We only process if the input is valid
    if (input.empty()) {
        throw std::invalid_argument("Invalid argument. Empty vector parameter.");
    }
    else {
        // We prepare an array of cstrings to pass to execvp
        // Plus an extra for the null terminated string
        cmd = new char* [input.size() + 1];
        size = input.size();

        // Iterate through the vector and copy the strings
        for (unsigned int i = 0; i < input.size(); i++) {
            cmd[i] = new char [input.at(i).size() + 1];
            std::strncpy(cmd[i], input.at(i).c_str(), input.at(i).size() + 1);
        }
        cmd[input.size()] = NULL;
    }
}

// Destructor

Command::~Command() {
    if (cmd) {
        // Delete the children first
        //for (int i = 0; cmd[i] != NULL; i++) {
        //delete cmd[i];
        //}
        // Now deallocate the parent
        delete [] cmd;
    }
}

// We are a leaf. We have no children.

Base * Command::get_left() {
    return NULL;
}

// We are a leaf. We have no children.

Base * Command::get_right() {
    return NULL;
}

// Since we have no children we do nothing

void Command::set_left(Base* left) {

}

// Since we have no children we do nothing

void Command::set_right(Base* right) {

}

// Always returns true. We are a leaf.

bool Command::isLeaf() {
    return true;
}

// Returns the contents of the command. If there are no
// commands returns an empty string

std::string Command::to_string() {
    std::string temp;
    if (cmd) {
        for (unsigned int i = 0; cmd[i] != NULL; i++) {
            temp += std::string(cmd[i]) + " ";
        }
    }
    return temp;
}

// Returns a vector of the contents. If there are no commands
// returns an empty vector.

std::vector<std::string> Command::to_vector() {
    std::vector<std::string> v;
    if (cmd) {

        for (unsigned int i = 0; cmd[i] != NULL; i++) {
            std::string temp(std::string(cmd[i]));
            v.push_back(temp);
        }
    }
    return v;
}
// We print out our data.

void Command::print() {
    if (cmd) {
        for (unsigned int i = 0; cmd[i] != NULL; i++) {
            std::cout << cmd[i] << " ";
        }
        std::cout << std::endl;
    }
}

// We execute

int Command::execute() {
    int returnValue;

    // Find which command to execute
    if (!std::strcmp(cmd[0], "exit")) {
        returnValue = exit_command();
    } else if (!std::strcmp(cmd[0], "test") || !std::strcmp(cmd[0], "[")) {
        returnValue = test_command();
    } else if (!std::strcmp(cmd[0], "cd")) {
        returnValue = cd_command();
    } else {
        returnValue = system_call();
    }

    // Return its value
    return returnValue;
}

// We issue the exit command

int Command::exit_command() {
    // We return -1 to signify exit.
    return -1;
}

// We issue the system call

int Command::system_call() {
    pid_t cpid, w; // pid of child and pid of process that has changed

    // Assuming the call to execv succeeds we set status = 0
    // and handle the result when call to execvp is made
    int status;

    int result; // final result after running execvp

    // Now we are almost ready to execute
    int exec_pipe[2]; // For communicating using a pipe

    // Create the pipe.
    if (pipe(exec_pipe)) {
        perror("pipe failed");
        return EXIT_FAILURE;
    }

    // We fork here and split into parent and child processes.
    if ((cpid = fork()) < 0) {
        perror("process failed");
        exit(EXIT_FAILURE);
    }// We are in the child process
    else if (cpid == 0) {

        // We close the read end of the pipe in the child
        close(exec_pipe[0]);

        // We call the fcntl function so that the child can set the FD_CLOEXEC flag
        // when exevp runs
        if (fcntl(exec_pipe[1], F_SETFD, fcntl(exec_pipe[1], F_GETFD) | FD_CLOEXEC)) {
            perror("fcntl failed");
            return EX_OSERR;
        }

        // Now we execute the command
        if ((status = execvp(cmd[0], cmd)) == -1) {
            // Now we write the error number result to the pipe
            write(exec_pipe[1], &errno, sizeof (int));
            std::string temp(cmd[0]);
            std::string s(": command not found");
            temp += s;
            perror(temp.c_str());
        }

    } else {
        // We are in the parent process now
        // We make parent process wait for child processes to exit
        do {

            // We close the writing end of the pipe in parent
            close(exec_pipe[1]);

            //If there was an error during wait we exit

            if ((w = waitpid(cpid, &status, WUNTRACED)) == -1) {
                perror("waitpid");
                exit(EXIT_FAILURE);
            }

        } while (!WIFEXITED(status) && !WIFSIGNALED(status));
    }

    // Return the error number the child process stored in the pipe after execvp.
    // If successfull it should return 0 or if it failed return the error number.
    result = read(exec_pipe[0], &errno, sizeof (errno));

    return result;
}

// We issue the test command

int Command::test_command() {
    //size_t size = PATH_MAX + 1;
    int status; // Return value after execution
    //char *buffer1 = new char[PATH_MAX + 1]; /* not sure about the "+ 1" */
    //char *buffer2 = new char[PATH_MAX+1]; /* not sure about the "+ 1" */
    //std::string temp("../");
    //temp += std::string(cmd[2]);

    //char *resolvedPath = realpath(cmd[2], buffer1);

    //char *currentDir = getcwd(buffer2, size);
    //if (resolvedPath) {

   // } else {
        //perror("Error");
    //}

    // We test whether we are dealing with
    struct stat sb;

    // We check how the test command was passed. Either it there were flags or no flags.
    if (!std::strcmp(cmd[1], "-e") || !std::strcmp(cmd[1], "-d")
            || !std::strcmp(cmd[1], "-f")) {
        // We test whether this is a file, directory or otherwise
        if (stat(cmd[2], &sb) == -1) {
            status = 1;
            //perror("stat"); // Outputs an error to the command line
        } else {
            // If it is either a directory or file
            if ((!std::strcmp(cmd[1], "-e")) && ((S_ISREG(sb.st_mode) == 1)
                    || (S_ISDIR(sb.st_mode) == 1))) {
                status = 0;
            }                // If it is a file only
            else if (!std::strcmp(cmd[1], "-f") && S_ISREG(sb.st_mode) == 1) {
                status = 0;
            }// If it is a directory only
            else if (!std::strcmp(cmd[1], "-d") && S_ISDIR(sb.st_mode) == 1) {
                status = 0;
            } else {
                status = 1;
            }
        }
    } else {
        // We test whether this is a file, directory or otherwise
        if (stat(cmd[1], &sb) == -1) {
            status = 1;
            //perror("stat"); // Outputs an error to the command line
        } else {
            // If it is either a directory or file we return true
            if (((S_ISREG(sb.st_mode) == 1) || (S_ISDIR(sb.st_mode) == 1))) {
                status = 0;
            } else {
                status = 1;
            }
        }
    }

    //delete buffer1;
    //delete buffer2;
    // We output the success of the test function
    if (status == 0) {
        std::cout << "(True)" << std::endl;
    } else {
        std::cout << "(False)" << std::endl;
    }
    return status;
}

int Command::cd_command() {
    char buffer[MAXPATHLEN];        //buffer for getcwd command
    const char *curPath = getcwd(buffer, MAXPATHLEN);       //get the current path
    const char *newPath;            //the new path

    //determine new path
    if (size == 1) {
        newPath = getenv("HOME");       //get the home path
    }
    else if (!std::strcmp(cmd[1], "-")) {
        newPath = getenv("OLDPWD");
    }
    else {
        newPath = cmd[1];
	if (cmd[1][0] == '~') {
		char *aNewPath = getenv("HOME");
		std::string afterTilde(cmd[1] + 1, strlen(cmd[1]) - 1);
		std::strcat(aNewPath, afterTilde.c_str());
		newPath = aNewPath;
	}
    }

    //perform data movement with error checking
    if (setenv("OLDPWD", curPath, 1) == -1) {   //set OLDPWD to current path
        perror("Could not update OLDPWD variable.");
        return 1;
    }
    if (setenv("PWD", newPath, 1) == -1) {  //set PWD to HOME path
        perror("Could not update PWD variable.");
        return 1;
    }
    if (chdir(newPath) == -1) {     //update current directory
        perror("Could not change directory.");
        return 1;
    }
        return 0;
}

<|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (C) 2014 Jolla Ltd.
** Contact: Vesa-Matti Hartikainen <vesa-matti.hartikainen@jolla.com>
**
****************************************************************************/

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

#include <QObject>
#include <QtTest>
#include <QtQml>
#include <QFile>
#include <QDesktopServices>
#include <QGuiApplication>
#include "declarativebookmarkmodel.h"
#include "testobject.h"

static const QByteArray QML_SNIPPET = \
        "import QtQuick 2.0\n" \
        "import Sailfish.Browser 1.0\n" \
        "Item {\n" \
        "   width: 100; height: 100\n" \
        "   property alias bookmarkModel: model\n" \
        "   BookmarkModel { id: model }\n" \
        "}\n";

class tst_declarativebookmarkmodel : public TestObject
{
    Q_OBJECT

public:
    tst_declarativebookmarkmodel();

private slots:
    void initTestCase();
    void cleanupTestCase();

    void addBookmark();
    void removeBookmark();
    void contains();

private:
    DeclarativeBookmarkModel *bookmarkModel;
};

tst_declarativebookmarkmodel::tst_declarativebookmarkmodel()
    : TestObject(QML_SNIPPET)
{
    bookmarkModel = TestObject::qmlObject<DeclarativeBookmarkModel>("bookmarkModel");
}

void tst_declarativebookmarkmodel::initTestCase()
{
    QVERIFY(bookmarkModel);

    // Preload bookmarks
    QCOMPARE(bookmarkModel->rowCount(), 4);
}

void tst_declarativebookmarkmodel::addBookmark()
{
    QSignalSpy countChangeSpy(bookmarkModel, SIGNAL(countChanged()));
    int count = bookmarkModel->rowCount() + 1;
    bookmarkModel->addBookmark("http://www.test1.jolla.com", "jolla", "");

    waitSignals(countChangeSpy, 1);
    QCOMPARE(bookmarkModel->rowCount(), count);
}

void tst_declarativebookmarkmodel::removeBookmark()
{
    bookmarkModel->addBookmark("http://www.test2.jolla.com", "jolla", "");
    int count = bookmarkModel->rowCount();

    QSignalSpy countChangeSpy(bookmarkModel, SIGNAL(countChanged()));
    bookmarkModel->removeBookmark("http://www.test2.jolla.com");
    waitSignals(countChangeSpy, 1);

    QCOMPARE(bookmarkModel->rowCount(), count-1);

    bookmarkModel->removeBookmark("http://www.test3.that.is.not.there.jolla.com");
    QCOMPARE(bookmarkModel->rowCount(), count-1);
}

void tst_declarativebookmarkmodel::contains()
{
    bookmarkModel->addBookmark("http://www.test4.contains.jolla.com", "jolla", "");

    QVERIFY(bookmarkModel->contains("http://www.test4.contains.jolla.com"));
    QVERIFY(!bookmarkModel->contains("http://www.test4.that.is.not.there.jolla.com"));
}

void tst_declarativebookmarkmodel::cleanupTestCase()
{
    QFile file(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/bookmarks.json");
    QVERIFY(file.remove());
}

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);
    app.setApplicationName("bookmarks-tests");
    app.setOrganizationName("org.sailfishos");
    app.setAttribute(Qt::AA_Use96Dpi, true);
    qmlRegisterType<DeclarativeBookmarkModel>("Sailfish.Browser", 1, 0, "BookmarkModel");
    tst_declarativebookmarkmodel testcase;
    return QTest::qExec(&testcase, argc, argv); \
}

#include "tst_declarativebookmarkmodel.moc"
<commit_msg>[sailfish-browser] Update default bookmark count for tests<commit_after>/****************************************************************************
**
** Copyright (C) 2014 Jolla Ltd.
** Contact: Vesa-Matti Hartikainen <vesa-matti.hartikainen@jolla.com>
**
****************************************************************************/

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

#include <QObject>
#include <QtTest>
#include <QtQml>
#include <QFile>
#include <QDesktopServices>
#include <QGuiApplication>
#include "declarativebookmarkmodel.h"
#include "testobject.h"

static const QByteArray QML_SNIPPET = \
        "import QtQuick 2.0\n" \
        "import Sailfish.Browser 1.0\n" \
        "Item {\n" \
        "   width: 100; height: 100\n" \
        "   property alias bookmarkModel: model\n" \
        "   BookmarkModel { id: model }\n" \
        "}\n";

class tst_declarativebookmarkmodel : public TestObject
{
    Q_OBJECT

public:
    tst_declarativebookmarkmodel();

private slots:
    void initTestCase();
    void cleanupTestCase();

    void addBookmark();
    void removeBookmark();
    void contains();

private:
    DeclarativeBookmarkModel *bookmarkModel;
};

tst_declarativebookmarkmodel::tst_declarativebookmarkmodel()
    : TestObject(QML_SNIPPET)
{
    bookmarkModel = TestObject::qmlObject<DeclarativeBookmarkModel>("bookmarkModel");
}

void tst_declarativebookmarkmodel::initTestCase()
{
    QVERIFY(bookmarkModel);

    // Preload bookmarks
    QCOMPARE(bookmarkModel->rowCount(), 5);
}

void tst_declarativebookmarkmodel::addBookmark()
{
    QSignalSpy countChangeSpy(bookmarkModel, SIGNAL(countChanged()));
    int count = bookmarkModel->rowCount() + 1;
    bookmarkModel->addBookmark("http://www.test1.jolla.com", "jolla", "");

    waitSignals(countChangeSpy, 1);
    QCOMPARE(bookmarkModel->rowCount(), count);
}

void tst_declarativebookmarkmodel::removeBookmark()
{
    bookmarkModel->addBookmark("http://www.test2.jolla.com", "jolla", "");
    int count = bookmarkModel->rowCount();

    QSignalSpy countChangeSpy(bookmarkModel, SIGNAL(countChanged()));
    bookmarkModel->removeBookmark("http://www.test2.jolla.com");
    waitSignals(countChangeSpy, 1);

    QCOMPARE(bookmarkModel->rowCount(), count-1);

    bookmarkModel->removeBookmark("http://www.test3.that.is.not.there.jolla.com");
    QCOMPARE(bookmarkModel->rowCount(), count-1);
}

void tst_declarativebookmarkmodel::contains()
{
    bookmarkModel->addBookmark("http://www.test4.contains.jolla.com", "jolla", "");

    QVERIFY(bookmarkModel->contains("http://www.test4.contains.jolla.com"));
    QVERIFY(!bookmarkModel->contains("http://www.test4.that.is.not.there.jolla.com"));
}

void tst_declarativebookmarkmodel::cleanupTestCase()
{
    QFile file(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/bookmarks.json");
    QVERIFY(file.remove());
}

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);
    app.setApplicationName("bookmarks-tests");
    app.setOrganizationName("org.sailfishos");
    app.setAttribute(Qt::AA_Use96Dpi, true);
    qmlRegisterType<DeclarativeBookmarkModel>("Sailfish.Browser", 1, 0, "BookmarkModel");
    tst_declarativebookmarkmodel testcase;
    return QTest::qExec(&testcase, argc, argv); \
}

#include "tst_declarativebookmarkmodel.moc"
<|endoftext|>