<commit_before>#include <tansa/action.h>
#include <tansa/control.h>
#include <tansa/core.h>
#include <tansa/gazebo.h>
#include <tansa/jocsParser.h>
#include <tansa/mocap.h>
#include <tansa/time.h>
#include <tansa/trajectory.h>
#include <tansa/vehicle.h>

#include <signal.h>
#include <unistd.h>

#include <vector>

using namespace tansa;
bool running;

void signal_sigint(int s) {
	running = false;
}

#define STATE_INIT 0
#define STATE_TAKEOFF 1
#define STATE_FLYING 2
#define STATE_LANDING 3

struct hardware_config {
	string clientAddress;
	string serverAddress;
};

int multidrone_main(int argc, char *argv[]) {

	assert(argc == 2);
	string configPath = argv[1];

	ifstream configStream(configPath);
	if (!configStream) throw "Unable to read config file!";

	/// Parse the config file
	std::string configData((std::istreambuf_iterator<char>(configStream)), std::istreambuf_iterator<char>());
	nlohmann::json rawJson = nlohmann::json::parse(configData);
	hardware_config config;
	string jocsPath = rawJson["jocsPath"];
	bool useMocap = rawJson["useMocap"];

	if (useMocap) {
		nlohmann::json hardwareConfig = rawJson["hardwareConfig"];
		config.clientAddress = hardwareConfig["clientAddress"];
		config.serverAddress = hardwareConfig["serverAddress"];
	}

	tansa::init();
	auto data = Jocs(jocsPath);
	auto actions = data.Parse();
	auto jocsHomes = data.GetHomes();
	std::vector<Point> spawns = jocsHomes;
	for(auto& s : spawns){
		s.z() = 0;
	}
	vector<Vector3d> homes = {
		{-1, 0, 1},
		{1, 0, 1},

	/*
		{0, -5, 1},
		{0, -3, 1},
		{0, -1, 1},
		{0, 1, 1},
		{0, 3, 1},
		{0, 5, 1}
	*/
	};

	Mocap *mocap;
	GazeboConnector *gazebo;

	if (useMocap) {
		mocap = new Mocap();
		mocap->connect(config.clientAddress, config.serverAddress);
	} else {
		gazebo = new GazeboConnector();
		gazebo->connect();
		gazebo->spawn(spawns);
	}

	int n = jocsHomes.size();

	vector<Vehicle *> vehicles(n);
	for(int i = 0; i < n; i++) {
		vehicles[i] = new Vehicle();
		vehicles[i]->connect(14550 + i*10, 14555 + i*10);
		if (useMocap) {
			mocap->track(vehicles[i], i+1);
		} else {
			gazebo->track(vehicles[i], i);
		}
	}

	sleep(4);

	vector<HoverController *> hovers(n);
	for(int i = 0; i < n; i++) {
		hovers[i] = new HoverController(vehicles[i], homes[i]);
	}

	vector<PositionController *> posctls(n);
	for(int i = 0; i < n; i++) {
		posctls[i] = new PositionController(vehicles[i]);
	}

	// Generate trajectories
	vector<vector<Trajectory *> > paths(n);
	for(int i = 0; i < n; i++) {
		Vector3d mag(0, 1.5, 0);
		if(i % 2 == 0)
			mag *= -1;

		paths[i].push_back(new LinearTrajectory(homes[i], 0, homes[i] + mag, 5));
		paths[i].push_back(new LinearTrajectory(homes[i] + mag, 5, homes[i] - mag, 10));
		paths[i].push_back(new LinearTrajectory(homes[i] - mag, 10, homes[i] + mag, 15));
		paths[i].push_back(new LinearTrajectory(homes[i] + mag, 15, homes[i] - mag, 20));
		paths[i].push_back(new LinearTrajectory(homes[i] - mag, 20, homes[i] + mag, 25));
		paths[i].push_back(new LinearTrajectory(homes[i] + mag, 25, homes[i] - mag, 30));

	}

	vector<Trajectory *> takeoffs(n);
	for(int i = 0; i < n; i++) {
		takeoffs[i] = new LinearTrajectory(vehicles[i]->state.position, 0, jocsHomes[i], 10.0);
	}

	int numLanded = 0;
	std::vector<int> states;
	states.resize(n);
	for(auto& state : states){
		state = STATE_INIT;
	}
	running = true;
	signal(SIGINT, signal_sigint);

	int i = 0;
	Time start(0,0);

	std::vector<int> plans;
	plans.resize(n);
	//This might zero memory already have to check if this is necessary
	for(auto& p : plans){
		p = 0;
	}

	printf("running...\n");

	Rate r(100);
	while(running) {

		//r.sleep();
		//continue;

		double t = Time::now().since(start).seconds();

		// Check for state transitions
		if(states[0] == STATE_INIT) {

			bool allGood = true;
			for(int vi = 0; vi < n; vi++) {
				if(vehicles[vi]->mode != "offboard" || !vehicles[vi]->armed) {
					allGood = false;
					break;
				}
			}

			if(allGood) {
				start = Time::now();
				for(auto& state :states) {
					state = STATE_TAKEOFF;
				}
				continue;
			}

		}
		else if(states[0] == STATE_TAKEOFF) {
			if(t >= 10.0) {
				for(auto& state : states){
					state = STATE_FLYING;
				}
				start = Time::now();
				continue;
			}

			/*
			bool allGood = true;
			for(int vi = 0; vi < n; vi++) {
				if(hovers[vi]->distance() > 0.1) {
					allGood = false;
					break;
				}
			}

			if(allGood) {
				start = Time::now();
				state = STATE_FLYING;
			}
			*/
		}

		// Do the control loops
		for(int vi = 0; vi < n; vi++) {
			Vehicle &v = *vehicles[vi];

			if(states[vi] == STATE_INIT) {
				// Lower frequency state management
				if(i % 50 == 0) {

					if(v.mode != "offboard") {
						v.set_mode("offboard");
						printf("Setting mode\n");
					}
					else if(!v.armed) {
						v.arm(true);
						printf("Arming mode\n");
					}
				}

				// Do nothing
				vehicles[vi]->setpoint_accel(Vector3d(0,0,0));
			}
			else if(states[vi] == STATE_TAKEOFF) {
				posctls[vi]->track(takeoffs[vi]);
				posctls[vi]->control(t);
			}
			else if(states[vi] == STATE_FLYING) {

				if(t >= actions[vi][plans[vi]]->GetEndTime()) {
					if(plans[vi] == actions[vi].size()-1) {
						states[vi] = STATE_LANDING;
						v.land();
						numLanded++;
						if(numLanded == n){
							running = false;
						}
						continue;
					}
					plans[vi]++;
				}
				Trajectory *cur = static_cast<MotionAction*>(actions[vi][plans[vi]])->GetPath();
				posctls[vi]->track(cur);
				posctls[vi]->control(t);
			}
		}
		r.sleep();
		i++;
	}

	/// Cleanup

	if (!useMocap) {
		gazebo->disconnect();
	}

	return 0;

}

int main(int argc, char *argv[]) {

	return multidrone_main(argc, argv);
	auto data = tansa::Jocs("singleDrone.jocs");
	auto actions = data.Parse();
	bool mocap_enabled = false,
		 sim_enabled = true;

	// TODO: Parse arguments here
	// -mocap to start mocap
	// -sim to use gazebo listeners

	tansa::init();

	Mocap *mocap = NULL;
	GazeboConnector *gazebo = NULL;

	Vehicle v;
	v.connect();

	// TODO: Ensure only one of these is enabled at a time
	if(sim_enabled) {
		gazebo = new GazeboConnector();
		gazebo->connect();
		gazebo->spawn({ {0,0,0} });
		gazebo->track(&v, 0);
		sleep(10); // Waiting for simulation to sync
	}
	if(mocap_enabled) {
		string client_addr = "192.168.1.161";
		string server_addr = "192.168.1.150";

		mocap = new Mocap();
		mocap->connect(client_addr, server_addr);
		mocap->track(&v, 1);

		// TODO: Have a better check for mocap initialization/health
		sleep(4);

	}

	running = true;
	signal(SIGINT, signal_sigint);

	// Points of a square
	vector<Point> points = {
		{1, 1, 1},
		{1, -1, 1},
		{-1, -1, 1},
		{-1, 1, 1},
		{0, 0, 1}
	};

	// Point in the air where the clock starts
	Point home(0, 0, 1);

	int state = STATE_INIT;

	int i = 0;

/*
	// For sample lighting demo
	float level = 0;
	float dl = 0.005;
*/

	HoverController hover(&v, home);
	PositionController posctl(&v);

	vector<Trajectory *> plan;
	int planI = 0; // Current part of the plan to execute
	double curT = 0.0; // Last time added to the plan (just used in the planning phase)

	CircleTrajectory circle(Point(0,0,1), 1, 0, 5.0, 2.0*M_PI, 10.0);
	TrajectoryState cS = circle.evaluate(circle.startTime());
	TrajectoryState cE = circle.evaluate(circle.endTime());

	// Enter into the circle
	plan.push_back(PolynomialTrajectory::compute(
		{ home }, 0.0,
		{ cS.position, cS.velocity, cS.acceleration }, 5.0
	));
	curT += 5;

	// Then do the full circle
	plan.push_back(&circle);
	curT = circle.endTime();

	// Exit the circle
	plan.push_back(PolynomialTrajectory::compute(
		{cE.position, cE.velocity, cE.acceleration}, curT,
		{points[0]}, curT + 2.5
	));
	curT += 2.5;

	// Finally do a square (and go back to the origin)
	for(int i = 1; i < points.size(); i++) {
		plan.push_back(new LinearTrajectory(points[i-1], curT, points[i], 5.0 + curT));
		curT += 5;
	}

	Trajectory *takeoff = new LinearTrajectory(v.state.position, 0, home, 10);

	Time start(0,0);
	Rate r(100);

	while(running) {
		//r.sleep();
		//continue;

/*
		Sample Lighting stuff

		v.set_lighting(level, level);

		level += dl;
		if(level >= 1.0 || level <= 0.0)
			dl = -dl;
*/

		if(state == STATE_INIT) {

			// Lower frequency state management
			if(i % 50 == 0) {
				if(v.mode != "offboard") {
					v.set_mode("offboard");
					printf("Setting mode\n");
				}
				else if(!v.armed) {
					v.arm(true);
					printf("Arming mode\n");
				}
				else {
					state = STATE_TAKEOFF;
					start = Time::now();
				}
			}

			posctl.track(takeoff);
			posctl.control(0);

		}
		else if(state == STATE_TAKEOFF) {

			double t = Time::now().since(start).seconds();

			posctl.track(takeoff);
			posctl.control(t);

			if(t >= 10.0) {
				start = Time::now();
				state = STATE_FLYING;
				printf("Flying...\n");
			}
		}
		else if(state == STATE_FLYING) {

			double t = Time::now().since(start).seconds();
			/*if(t >= plan[planI]->endTime())
				planI++;

			if(planI >= plan.size()) {
				state = STATE_LANDING;
				v.land();
				continue;
			}

			Trajectory *cur = plan[planI];*/

			if(t >= actions[0][planI]->GetEndTime())
				planI++;
			if(planI >= actions[0].size()){
				state = STATE_LANDING;
				v.land();
				continue;
			}
			Trajectory *cur = static_cast<MotionAction*>(actions[0][planI])->GetPath();
			posctl.track(cur);
			posctl.control(t);
		}
		else if(state == STATE_LANDING) {
			//h2->control(0.0);
			// TODO:
		}

		i++;

		r.sleep();
	}

	// Cleanup
	if(sim_enabled) {
		gazebo->disconnect();
	}

	// Stop all vehicles
	v.disconnect();

	printf("Done!\n");

}
<commit_msg>Remove single drone logic from gcs.<commit_after>#include <tansa/action.h>
#include <tansa/control.h>
#include <tansa/core.h>
#include <tansa/gazebo.h>
#include <tansa/jocsParser.h>
#include <tansa/mocap.h>

#include <unistd.h>

using namespace tansa;
bool running;

void signal_sigint(int s) {
	running = false;
}

#define STATE_INIT 0
#define STATE_TAKEOFF 1
#define STATE_FLYING 2
#define STATE_LANDING 3

struct hardware_config {
	string clientAddress;
	string serverAddress;
};

int main(int argc, char *argv[]) {

	assert(argc == 2);
	string configPath = argv[1];

	ifstream configStream(configPath);
	if (!configStream) throw "Unable to read config file!";

	/// Parse the config file
	std::string configData((std::istreambuf_iterator<char>(configStream)), std::istreambuf_iterator<char>());
	nlohmann::json rawJson = nlohmann::json::parse(configData);
	hardware_config config;
	string jocsPath = rawJson["jocsPath"];
	bool useMocap = rawJson["useMocap"];

	if (useMocap) {
		nlohmann::json hardwareConfig = rawJson["hardwareConfig"];
		config.clientAddress = hardwareConfig["clientAddress"];
		config.serverAddress = hardwareConfig["serverAddress"];
	}

	tansa::init();
	auto data = Jocs(jocsPath);
	auto actions = data.Parse();
	auto jocsHomes = data.GetHomes();
	std::vector<Point> spawns = jocsHomes;
	for(auto& s : spawns){
		s.z() = 0;
	}
	vector<Vector3d> homes = {
		{-1, 0, 1},
		{1, 0, 1},

	/*
		{0, -5, 1},
		{0, -3, 1},
		{0, -1, 1},
		{0, 1, 1},
		{0, 3, 1},
		{0, 5, 1}
	*/
	};

	Mocap *mocap;
	GazeboConnector *gazebo;

	if (useMocap) {
		mocap = new Mocap();
		mocap->connect(config.clientAddress, config.serverAddress);
	} else {
		gazebo = new GazeboConnector();
		gazebo->connect();
		gazebo->spawn(spawns);
		sleep(10); // Waiting for simulation to sync
	}

	int n = jocsHomes.size();

	vector<Vehicle *> vehicles(n);
	for(int i = 0; i < n; i++) {
		vehicles[i] = new Vehicle();
		vehicles[i]->connect(14550 + i*10, 14555 + i*10);
		if (useMocap) {
			mocap->track(vehicles[i], i+1);
		} else {
			gazebo->track(vehicles[i], i);
		}
	}

	// TODO: Have a better check for mocap initialization/health
	sleep(4);

	vector<HoverController *> hovers(n);
	for(int i = 0; i < n; i++) {
		hovers[i] = new HoverController(vehicles[i], homes[i]);
	}

	vector<PositionController *> posctls(n);
	for(int i = 0; i < n; i++) {
		posctls[i] = new PositionController(vehicles[i]);
	}

	// Generate trajectories
	vector<vector<Trajectory *> > paths(n);
	for(int i = 0; i < n; i++) {
		Vector3d mag(0, 1.5, 0);
		if(i % 2 == 0)
			mag *= -1;

		paths[i].push_back(new LinearTrajectory(homes[i], 0, homes[i] + mag, 5));
		paths[i].push_back(new LinearTrajectory(homes[i] + mag, 5, homes[i] - mag, 10));
		paths[i].push_back(new LinearTrajectory(homes[i] - mag, 10, homes[i] + mag, 15));
		paths[i].push_back(new LinearTrajectory(homes[i] + mag, 15, homes[i] - mag, 20));
		paths[i].push_back(new LinearTrajectory(homes[i] - mag, 20, homes[i] + mag, 25));
		paths[i].push_back(new LinearTrajectory(homes[i] + mag, 25, homes[i] - mag, 30));

	}

	vector<Trajectory *> takeoffs(n);
	for(int i = 0; i < n; i++) {
		takeoffs[i] = new LinearTrajectory(vehicles[i]->state.position, 0, jocsHomes[i], 10.0);
	}

	int numLanded = 0;
	std::vector<int> states;
	states.resize(n);
	for(auto& state : states){
		state = STATE_INIT;
	}
	running = true;
	signal(SIGINT, signal_sigint);

	int i = 0;

	/*
	// For sample lighting demo
	float level = 0;
	float dl = 0.005;
	*/

	Time start(0,0);

	std::vector<int> plans;
	plans.resize(n);
	//This might zero memory already have to check if this is necessary
	for(auto& p : plans){
		p = 0;
	}

	printf("running...\n");

	Rate r(100);
	while(running) {

		//r.sleep();
		//continue;

		double t = Time::now().since(start).seconds();

		// Check for state transitions
		if(states[0] == STATE_INIT) {

			bool allGood = true;
			for(int vi = 0; vi < n; vi++) {
				if(vehicles[vi]->mode != "offboard" || !vehicles[vi]->armed) {
					allGood = false;
					break;
				}
			}

			if(allGood) {
				start = Time::now();
				for(auto& state :states) {
					state = STATE_TAKEOFF;
				}
				continue;
			}

		}
		else if(states[0] == STATE_TAKEOFF) {
			if(t >= 10.0) {
				for(auto& state : states){
					state = STATE_FLYING;
				}
				start = Time::now();
				continue;
			}

			/*
			bool allGood = true;
			for(int vi = 0; vi < n; vi++) {
				if(hovers[vi]->distance() > 0.1) {
					allGood = false;
					break;
				}
			}

			if(allGood) {
				start = Time::now();
				state = STATE_FLYING;
			}
			*/
		}

		// Do the control loops
		for(int vi = 0; vi < n; vi++) {
			Vehicle &v = *vehicles[vi];

			/*
			Sample Lighting stuff

			v.set_lighting(level, level);

			level += dl;
			if(level >= 1.0 || level <= 0.0)
				dl = -dl;
			*/

			if(states[vi] == STATE_INIT) {
				// Lower frequency state management
				if(i % 50 == 0) {

					if(v.mode != "offboard") {
						v.set_mode("offboard");
						printf("Setting mode\n");
					}
					else if(!v.armed) {
						v.arm(true);
						printf("Arming mode\n");
					}
				}

				// Do nothing
				vehicles[vi]->setpoint_accel(Vector3d(0,0,0));
			}
			else if(states[vi] == STATE_TAKEOFF) {
				posctls[vi]->track(takeoffs[vi]);
				posctls[vi]->control(t);
			}
			else if(states[vi] == STATE_FLYING) {

				if(t >= actions[vi][plans[vi]]->GetEndTime()) {
					if(plans[vi] == actions[vi].size()-1) {
						states[vi] = STATE_LANDING;
						v.land();
						numLanded++;
						if(numLanded == n){
							running = false;
						}
						continue;
					}
					plans[vi]++;
				}
				Trajectory *cur = static_cast<MotionAction*>(actions[vi][plans[vi]])->GetPath();
				posctls[vi]->track(cur);
				posctls[vi]->control(t);
			}
		}
		r.sleep();
		i++;
	}

	/// Cleanup

	if (!useMocap) {
		gazebo->disconnect();
	}

	// Stop all vehicles
	for(int vi = 0; vi < n; vi++) {
		Vehicle &v = *vehicles[vi];
		v.disconnect();
	}

	printf("Done!\n");
}
<|endoftext|>


<commit_before>#include "ast.hpp"

ASTNode::ASTNode() {}
ASTNode::~ASTNode() {};

void ASTNode::addChild(Link child) {
  child->setParent(shared_from_this());
  children.push_back(child);
}

ASTNode::Link ASTNode::removeChild(int64 pos) {
  Link child = this->at(pos);
  child->setParent(WeakLink());
  this->children.erase(children.begin() + transformArrayIndex(pos));
  return child;
}

std::size_t ASTNode::transformArrayIndex(int64 idx) const {
  std::size_t res = idx;
  if (idx < 0) {
    res = children.size() + idx; // Negative indices count from the end of the vector
  }
  if (res > children.size()) {
    throw InternalError("Index out of array bounds", {
      METADATA_PAIRS,
      {"index", std::to_string(idx)},
      {"calculated", std::to_string(res)}
    });
  }
  return res;
}

ASTNode::Children ASTNode::getChildren() const {
  return children;
}

ASTNode::Link ASTNode::at(int64 pos) const {
  return children.at(transformArrayIndex(pos));
}

void ASTNode::setParent(WeakLink newParent) {parent = newParent;}
ASTNode::WeakLink ASTNode::getParent() const {return parent;}

void ASTNode::setTrace(Trace trace) {
  this->trace = trace;
}
Trace ASTNode::getTrace() const {
  return trace;
}

ASTNode::Link ASTNode::findAbove(std::function<bool(Link)> isOk) const {
  auto lastParent = this->getParent();
  for (; ; lastParent = lastParent.lock()->getParent()) {
    if (lastParent.lock() == nullptr) return nullptr;
    if (isOk(lastParent.lock())) return lastParent.lock();
  }
}

bool ASTNode::operator==(const ASTNode& rhs) const {
  if (typeid(*this) != typeid(rhs)) return false;
  if (children.size() != rhs.getChildren().size()) return false;
  for (uint64 i = 0; i < children.size(); i++) {
    if (this->at(i) == nullptr && rhs.at(i) == nullptr) continue;
    if (*(this->at(i)) != *(rhs.at(i))) return false;
  }
  return true;
}

bool ASTNode::operator!=(const ASTNode& rhs) const {
  return !operator==(rhs);
}

NoMoreChildrenNode::NoMoreChildrenNode(int childrenCount) {
  children.resize(childrenCount, nullptr);
}

BlockNode::BlockNode(BlockType type): type(type) {}

BlockType BlockNode::getType() const {
  return type;
}

ExpressionNode::ExpressionNode(Token token): tok(token) {
  switch (tok.type) {
    case IDENTIFIER:
    case OPERATOR:
    case L_INTEGER:
    case L_FLOAT:
    case L_STRING:
    case L_BOOLEAN:
      break;
    default: throw InternalError("Trying to add unsupported token to ExpressionNode", {METADATA_PAIRS, {"token", token.toString()}});
  }
}

std::shared_ptr<ExpressionNode> ExpressionNode::at(int64 pos) const {
  return std::dynamic_pointer_cast<ExpressionNode>(ASTNode::at(pos));
}

Token ExpressionNode::getToken() const {
  return tok;
}

DeclarationNode::DeclarationNode(std::string identifier, TypeList typeList):
  NoMoreChildrenNode(1), identifier(identifier), info(typeList) {}

std::string DeclarationNode::getIdentifier() const {
  return identifier;
}

DefiniteTypeInfo DeclarationNode::getTypeInfo() const {
  return info;
}

bool DeclarationNode::isDynamic() const {
  return info.isDynamic();
}

bool DeclarationNode::hasInit() const {
  return children[0] != nullptr;
}

TypeNode::TypeNode(std::string name, TypeList inheritsFrom): name(name), inheritsFrom(inheritsFrom) {}

std::string TypeNode::getName() const {
  return name;
}

TypeList TypeNode::getAncestors() const {
  return inheritsFrom;
}

TypeData* TypeNode::getTyData() const{
  return typeData;
}

void TypeNode::setTyData(TypeData* newData) {
  typeData = newData;
}

void TypeNode::addChild(Link child) {
  if (
    Node<ConstructorNode>::dynPtrCast(child) ||
    Node<MethodNode>::dynPtrCast(child) ||
    Node<MemberNode>::dynPtrCast(child)
  ) {
    ASTNode::addChild(child);
  } else {
    throw InternalError("Attempt to add invalid type to TypeNode", {
      METADATA_PAIRS,
      {"valid types", "ConstructorNode MethodNode MemberNode"}
    });
  }
}

BranchNode::BranchNode(): NoMoreChildrenNode(3) {}

LoopNode::LoopNode(): NoMoreChildrenNode(4) {}

llvm::BasicBlock* LoopNode::getExitBlock() const {
  return exitBlock;
}
void LoopNode::setExitBlock(llvm::BasicBlock* bb) {
  this->exitBlock = bb;
}

ReturnNode::ReturnNode(): NoMoreChildrenNode(1) {}

BreakLoopNode::BreakLoopNode(): NoMoreChildrenNode(0) {}

FunctionNode::FunctionNode(std::string ident, FunctionSignature sig, bool foreign):
  NoMoreChildrenNode(1),
  ident(ident),
  sig(sig),
  foreign(foreign) {}
FunctionNode::FunctionNode(FunctionSignature sig): FunctionNode("", sig, false) {}

std::string FunctionNode::getIdentifier() const {
  return ident;
}
const FunctionSignature& FunctionNode::getSignature() const {
  return sig;
}
bool FunctionNode::isForeign() const {
  return foreign;
}
bool FunctionNode::isAnon() const {
  return ident.empty();
}

ConstructorNode::ConstructorNode(FunctionSignature::Arguments args, Visibility vis):
  FunctionNode(FunctionSignature(TypeInfo(nullptr), args)), vis(vis) {
  if (vis == INVALID) throw InternalError("Invalid visibility", {METADATA_PAIRS});
}

Visibility ConstructorNode::getVisibility() const {
  return vis;
}

MethodNode::MethodNode(std::string name, FunctionSignature sig, Visibility vis, bool staticM, bool isForeign):
  FunctionNode(name, sig, isForeign), vis(vis), staticM(staticM) {
  if (vis == INVALID) throw InternalError("Invalid visibility", {METADATA_PAIRS});
}

Visibility MethodNode::getVisibility() const {
  return vis;
}

bool MethodNode::isStatic() const {
  return staticM;
}

MemberNode::MemberNode(std::string identifier, TypeList typeList, bool staticM, Visibility vis):
  DeclarationNode(identifier, typeList), staticM(staticM), vis(vis) {}

bool MemberNode::isStatic() const {
  return staticM;
}

Visibility MemberNode::getVisibility() const {
  return vis;
}

/**
  \brief Macros for easy implementation of getters and setters for NoMoreChildrenNode subclasses

  Omologues exist in header to provide signatures for these implementations
  \param srcNode name of subclass
  \param childIndex index for the child (because number of children is fixed on NoMoreChildrenNodes)
  \param nameOf name for getter/setter (get##nameOf and set##nameOf)
  \param linkType type of child
*/
#define GET_FOR(srcNode, childIndex, nameOf, linkType) \
Node<linkType>::Link srcNode::get##nameOf() const {\
  return Node<linkType>::staticPtrCast(children[childIndex]);\
}
/// \copydoc GET_FOR
#define SET_FOR(srcNode, childIndex, nameOf, linkType) \
void srcNode::set##nameOf(std::shared_ptr<linkType> newNode) {\
  newNode->setParent(shared_from_this());\
  children[childIndex] = newNode;\
}
/// \copydoc GET_FOR
#define GET_SET_FOR(srcNode, childIndex, nameOf, linkType) \
GET_FOR(srcNode, childIndex, nameOf, linkType) \
SET_FOR(srcNode, childIndex, nameOf, linkType)

GET_SET_FOR(DeclarationNode, 0, Init, ExpressionNode)

GET_SET_FOR(BranchNode, 0, Condition, ExpressionNode)
GET_SET_FOR(BranchNode, 1, SuccessBlock, BlockNode)
GET_FOR(BranchNode, 2, FailiureBlock, ASTNode)
SET_FOR(BranchNode, 2, FailiureBlock, BlockNode)
SET_FOR(BranchNode, 2, FailiureBlock, BranchNode)

GET_SET_FOR(LoopNode, 0, Init, DeclarationNode)
GET_SET_FOR(LoopNode, 1, Condition, ExpressionNode)
GET_SET_FOR(LoopNode, 2, Update, ExpressionNode)
GET_SET_FOR(LoopNode, 3, Code, BlockNode)

GET_SET_FOR(ReturnNode, 0, Value, ExpressionNode)

GET_SET_FOR(FunctionNode, 0, Code, BlockNode)

#undef GET_FOR
#undef SET_FOR
#undef GET_SET_FOR

// AST class

AST::AST(Node<BlockNode>::Link lk): root(lk) {}

void AST::print() const {
  root->printTree(0);
}

Node<BlockNode>::Link AST::getRoot() const {
  return root;
}

bool AST::operator==(const AST& rhs) const {
  if (*this->root.get() != *rhs.root.get()) return false;
  return true;
}
bool AST::operator!=(const AST& rhs) const {
  return !operator==(rhs);
}
<commit_msg>Fix alignment<commit_after>#include "ast.hpp"

ASTNode::ASTNode() {}
ASTNode::~ASTNode() {};

void ASTNode::addChild(Link child) {
  child->setParent(shared_from_this());
  children.push_back(child);
}

ASTNode::Link ASTNode::removeChild(int64 pos) {
  Link child = this->at(pos);
  child->setParent(WeakLink());
  this->children.erase(children.begin() + transformArrayIndex(pos));
  return child;
}

std::size_t ASTNode::transformArrayIndex(int64 idx) const {
  std::size_t res = idx;
  if (idx < 0) {
    res = children.size() + idx; // Negative indices count from the end of the vector
  }
  if (res > children.size()) {
    throw InternalError("Index out of array bounds", {
      METADATA_PAIRS,
      {"index", std::to_string(idx)},
      {"calculated", std::to_string(res)}
    });
  }
  return res;
}

ASTNode::Children ASTNode::getChildren() const {
  return children;
}

ASTNode::Link ASTNode::at(int64 pos) const {
  return children.at(transformArrayIndex(pos));
}

void ASTNode::setParent(WeakLink newParent) {parent = newParent;}
ASTNode::WeakLink ASTNode::getParent() const {return parent;}

void ASTNode::setTrace(Trace trace) {
  this->trace = trace;
}
Trace ASTNode::getTrace() const {
  return trace;
}

ASTNode::Link ASTNode::findAbove(std::function<bool(Link)> isOk) const {
  auto lastParent = this->getParent();
  for (; ; lastParent = lastParent.lock()->getParent()) {
    if (lastParent.lock() == nullptr) return nullptr;
    if (isOk(lastParent.lock())) return lastParent.lock();
  }
}

bool ASTNode::operator==(const ASTNode& rhs) const {
  if (typeid(*this) != typeid(rhs)) return false;
  if (children.size() != rhs.getChildren().size()) return false;
  for (uint64 i = 0; i < children.size(); i++) {
    if (this->at(i) == nullptr && rhs.at(i) == nullptr) continue;
    if (*(this->at(i)) != *(rhs.at(i))) return false;
  }
  return true;
}

bool ASTNode::operator!=(const ASTNode& rhs) const {
  return !operator==(rhs);
}

NoMoreChildrenNode::NoMoreChildrenNode(int childrenCount) {
  children.resize(childrenCount, nullptr);
}

BlockNode::BlockNode(BlockType type): type(type) {}

BlockType BlockNode::getType() const {
  return type;
}

ExpressionNode::ExpressionNode(Token token): tok(token) {
  switch (tok.type) {
    case IDENTIFIER:
    case OPERATOR:
    case L_INTEGER:
    case L_FLOAT:
    case L_STRING:
    case L_BOOLEAN:
      break;
    default: throw InternalError("Trying to add unsupported token to ExpressionNode", {METADATA_PAIRS, {"token", token.toString()}});
  }
}

std::shared_ptr<ExpressionNode> ExpressionNode::at(int64 pos) const {
  return std::dynamic_pointer_cast<ExpressionNode>(ASTNode::at(pos));
}

Token ExpressionNode::getToken() const {
  return tok;
}

DeclarationNode::DeclarationNode(std::string identifier, TypeList typeList):
  NoMoreChildrenNode(1), identifier(identifier), info(typeList) {}

std::string DeclarationNode::getIdentifier() const {
  return identifier;
}

DefiniteTypeInfo DeclarationNode::getTypeInfo() const {
  return info;
}

bool DeclarationNode::isDynamic() const {
  return info.isDynamic();
}

bool DeclarationNode::hasInit() const {
  return children[0] != nullptr;
}

TypeNode::TypeNode(std::string name, TypeList inheritsFrom):
  name(name),
  inheritsFrom(inheritsFrom) {}

std::string TypeNode::getName() const {
  return name;
}

TypeList TypeNode::getAncestors() const {
  return inheritsFrom;
}

TypeData* TypeNode::getTyData() const{
  return typeData;
}

void TypeNode::setTyData(TypeData* newData) {
  typeData = newData;
}

void TypeNode::addChild(Link child) {
  if (
    Node<ConstructorNode>::dynPtrCast(child) ||
    Node<MethodNode>::dynPtrCast(child) ||
    Node<MemberNode>::dynPtrCast(child)
  ) {
    ASTNode::addChild(child);
  } else {
    throw InternalError("Attempt to add invalid type to TypeNode", {
      METADATA_PAIRS,
      {"valid types", "ConstructorNode MethodNode MemberNode"}
    });
  }
}

BranchNode::BranchNode(): NoMoreChildrenNode(3) {}

LoopNode::LoopNode(): NoMoreChildrenNode(4) {}

llvm::BasicBlock* LoopNode::getExitBlock() const {
  return exitBlock;
}
void LoopNode::setExitBlock(llvm::BasicBlock* bb) {
  this->exitBlock = bb;
}

ReturnNode::ReturnNode(): NoMoreChildrenNode(1) {}

BreakLoopNode::BreakLoopNode(): NoMoreChildrenNode(0) {}

FunctionNode::FunctionNode(std::string ident, FunctionSignature sig, bool foreign):
  NoMoreChildrenNode(1),
  ident(ident),
  sig(sig),
  foreign(foreign) {}
FunctionNode::FunctionNode(FunctionSignature sig): FunctionNode("", sig, false) {}

std::string FunctionNode::getIdentifier() const {
  return ident;
}
const FunctionSignature& FunctionNode::getSignature() const {
  return sig;
}
bool FunctionNode::isForeign() const {
  return foreign;
}
bool FunctionNode::isAnon() const {
  return ident.empty();
}

ConstructorNode::ConstructorNode(FunctionSignature::Arguments args, Visibility vis):
  FunctionNode(FunctionSignature(TypeInfo(nullptr), args)), vis(vis) {
  if (vis == INVALID) throw InternalError("Invalid visibility", {METADATA_PAIRS});
}

Visibility ConstructorNode::getVisibility() const {
  return vis;
}

MethodNode::MethodNode(std::string name, FunctionSignature sig, Visibility vis, bool staticM, bool isForeign):
  FunctionNode(name, sig, isForeign), vis(vis), staticM(staticM) {
  if (vis == INVALID) throw InternalError("Invalid visibility", {METADATA_PAIRS});
}

Visibility MethodNode::getVisibility() const {
  return vis;
}

bool MethodNode::isStatic() const {
  return staticM;
}

MemberNode::MemberNode(std::string identifier, TypeList typeList, bool staticM, Visibility vis):
  DeclarationNode(identifier, typeList), staticM(staticM), vis(vis) {}

bool MemberNode::isStatic() const {
  return staticM;
}

Visibility MemberNode::getVisibility() const {
  return vis;
}

/**
  \brief Macros for easy implementation of getters and setters for NoMoreChildrenNode subclasses

  Omologues exist in header to provide signatures for these implementations
  \param srcNode name of subclass
  \param childIndex index for the child (because number of children is fixed on NoMoreChildrenNodes)
  \param nameOf name for getter/setter (get##nameOf and set##nameOf)
  \param linkType type of child
*/
#define GET_FOR(srcNode, childIndex, nameOf, linkType) \
Node<linkType>::Link srcNode::get##nameOf() const {\
  return Node<linkType>::staticPtrCast(children[childIndex]);\
}
/// \copydoc GET_FOR
#define SET_FOR(srcNode, childIndex, nameOf, linkType) \
void srcNode::set##nameOf(std::shared_ptr<linkType> newNode) {\
  newNode->setParent(shared_from_this());\
  children[childIndex] = newNode;\
}
/// \copydoc GET_FOR
#define GET_SET_FOR(srcNode, childIndex, nameOf, linkType) \
GET_FOR(srcNode, childIndex, nameOf, linkType) \
SET_FOR(srcNode, childIndex, nameOf, linkType)

GET_SET_FOR(DeclarationNode, 0, Init, ExpressionNode)

GET_SET_FOR(BranchNode, 0, Condition, ExpressionNode)
GET_SET_FOR(BranchNode, 1, SuccessBlock, BlockNode)
GET_FOR(BranchNode, 2, FailiureBlock, ASTNode)
SET_FOR(BranchNode, 2, FailiureBlock, BlockNode)
SET_FOR(BranchNode, 2, FailiureBlock, BranchNode)

GET_SET_FOR(LoopNode, 0, Init, DeclarationNode)
GET_SET_FOR(LoopNode, 1, Condition, ExpressionNode)
GET_SET_FOR(LoopNode, 2, Update, ExpressionNode)
GET_SET_FOR(LoopNode, 3, Code, BlockNode)

GET_SET_FOR(ReturnNode, 0, Value, ExpressionNode)

GET_SET_FOR(FunctionNode, 0, Code, BlockNode)

#undef GET_FOR
#undef SET_FOR
#undef GET_SET_FOR

// AST class

AST::AST(Node<BlockNode>::Link lk): root(lk) {}

void AST::print() const {
  root->printTree(0);
}

Node<BlockNode>::Link AST::getRoot() const {
  return root;
}

bool AST::operator==(const AST& rhs) const {
  if (*this->root.get() != *rhs.root.get()) return false;
  return true;
}
bool AST::operator!=(const AST& rhs) const {
  return !operator==(rhs);
}
<|endoftext|>


<commit_before>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "gdef.h"

#include <vector>

#include "layout.h"
#include "maxp.h"

// GDEF - The Glyph Definition Table
// http://www.microsoft.com/typography/otspec/gdef.htm

namespace {

// The maximum class value in class definition tables.
const uint16_t kMaxClassDefValue = 0xFFFF;
// The maximum class value in the glyph class definision table.
const uint16_t kMaxGlyphClassDefValue = 4;
// The maximum format number of caret value tables.
// We don't support format 3 for now. See the comment in
// ParseLigCaretListTable() for the reason.
const uint16_t kMaxCaretValueFormat = 2;

bool ParseGlyphClassDefTable(ots::OpenTypeFile *file, const uint8_t *data,
                             size_t length, const uint16_t num_glyphs) {
  return ots::ParseClassDefTable(data, length, num_glyphs,
                                 kMaxGlyphClassDefValue);
}

bool ParseAttachListTable(ots::OpenTypeFile *file, const uint8_t *data,
                          size_t length, const uint16_t num_glyphs) {
  ots::Buffer subtable(data, length);

  uint16_t offset_coverage = 0;
  uint16_t glyph_count = 0;
  if (!subtable.ReadU16(&offset_coverage) ||
      !subtable.ReadU16(&glyph_count)) {
    return OTS_FAILURE();
  }
  const unsigned attach_points_end = static_cast<unsigned>(4) + 2*glyph_count;
  if (offset_coverage == 0 || offset_coverage >= length ||
      offset_coverage < attach_points_end) {
    return OTS_FAILURE();
  }
  if (glyph_count > num_glyphs) {
    OTS_WARNING("bad glyph count: %u", glyph_count);
    return OTS_FAILURE();
  }

  std::vector<uint16_t> attach_points;
  attach_points.resize(glyph_count);
  for (unsigned i = 0; i < glyph_count; ++i) {
    if (!subtable.ReadU16(&attach_points[i])) {
      return OTS_FAILURE();
    }
    if (attach_points[i] >= length ||
        attach_points[i] < attach_points_end) {
      return OTS_FAILURE();
    }
  }

  // Parse coverage table
  if (!ots::ParseCoverageTable(data + offset_coverage,
                               length - offset_coverage, num_glyphs)) {
    return OTS_FAILURE();
  }

  // Parse attach point table
  for (unsigned i = 0; i < attach_points.size(); ++i) {
    subtable.set_offset(attach_points[i]);
    uint16_t point_count = 0;
    if (!subtable.ReadU16(&point_count)) {
      return OTS_FAILURE();
    }
    if (point_count == 0) {
      return OTS_FAILURE();
    }
    uint16_t last_point_index = 0;
    uint16_t point_index = 0;
    for (unsigned j = 0; j < point_count; ++j) {
      if (!subtable.ReadU16(&point_index)) {
        return OTS_FAILURE();
      }
      // Contour point indeces are in increasing numerical order
      if (last_point_index != 0 && last_point_index >= point_index) {
        OTS_WARNING("bad contour indeces: %u >= %u",
                    last_point_index, point_index);
        return OTS_FAILURE();
      }
      last_point_index = point_index;
    }
  }
  return true;
}

bool ParseLigCaretListTable(ots::OpenTypeFile *file, const uint8_t *data,
                            size_t length, const uint16_t num_glyphs) {
  ots::Buffer subtable(data, length);
  uint16_t offset_coverage = 0;
  uint16_t lig_glyph_count = 0;
  if (!subtable.ReadU16(&offset_coverage) ||
      !subtable.ReadU16(&lig_glyph_count)) {
    return OTS_FAILURE();
  }
  const unsigned lig_glyphs_end = static_cast<unsigned>(4) + 2*lig_glyph_count;
  if (offset_coverage == 0 || offset_coverage >= length ||
      offset_coverage < lig_glyphs_end) {
    return OTS_FAILURE();
  }
  if (lig_glyph_count > num_glyphs) {
    OTS_WARNING("bad ligature glyph count: %u", lig_glyph_count);
    return OTS_FAILURE();
  }

  std::vector<uint16_t> lig_glyphs;
  lig_glyphs.resize(lig_glyph_count);
  for (unsigned i = 0; i < lig_glyph_count; ++i) {
    if (!subtable.ReadU16(&lig_glyphs[i])) {
      return OTS_FAILURE();
    }
    if (lig_glyphs[i] >= length || lig_glyphs[i] < lig_glyphs_end) {
      return OTS_FAILURE();
    }
  }

  // Parse coverage table
  if (!ots::ParseCoverageTable(data + offset_coverage,
                               length - offset_coverage, num_glyphs)) {
    return OTS_FAILURE();
  }

  // Parse ligature glyph table
  for (unsigned i = 0; i < lig_glyphs.size(); ++i) {
    subtable.set_offset(lig_glyphs[i]);
    uint16_t caret_count = 0;
    if (!subtable.ReadU16(&caret_count)) {
      return OTS_FAILURE();
    }
    if (caret_count == 0) {
      OTS_WARNING("bad caret value count: %u", caret_count);
      return OTS_FAILURE();
    }

    std::vector<uint16_t> caret_values;
    caret_values.resize(caret_count);
    uint16_t last_offset_caret = 0;
    unsigned caret_values_end = static_cast<unsigned>(2) * 2*caret_count;
    for (unsigned j = 0; j < caret_count; ++j) {
      if (!subtable.ReadU16(&caret_values[j])) {
        return OTS_FAILURE();
      }
      if (caret_values[j] >= length || caret_values[j] < caret_values_end) {
        return OTS_FAILURE();
      }
      // Caret offsets are in increasing coordinate order
      if (last_offset_caret != 0 && last_offset_caret >= caret_values[j]) {
        OTS_WARNING("offset isn't in increasing coordinate order: %u >= %u",
                    last_offset_caret, caret_values[j]);
        return OTS_FAILURE();
      }
      last_offset_caret = caret_values[j];
    }

    // Parse caret values table
    for (unsigned j = 0; j < caret_count; ++j) {
      subtable.set_offset(lig_glyphs[i] + caret_values[j]);
      uint16_t caret_format = 0;
      if (!subtable.ReadU16(&caret_format)) {
        return OTS_FAILURE();
      }
      // TODO(bashi): We only support caret value format 1 and 2 for now
      // because there are no fonts which contain caret value format 3
      // as far as we investigated.
      if (caret_format == 0 || caret_format > kMaxCaretValueFormat) {
        OTS_WARNING("bad caret value format: %u", caret_format);
        return OTS_FAILURE();
      }
      // CaretValueFormats contain a 2-byte field which could be
      // arbitrary value.
      if (!subtable.Skip(2)) {
        return OTS_FAILURE();
      }
    }
  }
  return true;
}

bool ParseMarkAttachClassDefTable(ots::OpenTypeFile *file, const uint8_t *data,
                                  size_t length, const uint16_t num_glyphs) {
  return ots::ParseClassDefTable(data, length, num_glyphs, kMaxClassDefValue);
}

bool ParseMarkGlyphSetsDefTable(ots::OpenTypeFile *file, const uint8_t *data,
                                size_t length, const uint16_t num_glyphs) {
  ots::Buffer subtable(data, length);
  uint16_t format = 0;
  uint16_t mark_set_count = 0;
  if (!subtable.ReadU16(&format) ||
      !subtable.ReadU16(&mark_set_count)) {
    return OTS_FAILURE();
  }
  if (format != 1) {
    OTS_WARNING("bad mark glyph set table format: %u", format);
    return OTS_FAILURE();
  }

  const unsigned mark_sets_end = static_cast<unsigned>(4) + 2*mark_set_count;
  for (unsigned i = 0; i < mark_set_count; ++i) {
    uint32_t offset_coverage = 0;
    if (!subtable.ReadU32(&offset_coverage)) {
      return OTS_FAILURE();
    }
    if (offset_coverage >= length ||
        offset_coverage < mark_sets_end) {
      return OTS_FAILURE();
    }
    if (!ots::ParseCoverageTable(data + offset_coverage,
                                 length - offset_coverage, num_glyphs)) {
      return OTS_FAILURE();
    }
  }
  return true;
}

}  // namespace

#define DROP_THIS_TABLE \
  do { delete file->gdef; file->gdef = 0; } while (0)

namespace ots {

bool ots_gdef_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
  // Grab the number of glyphs in the file from the maxp table to check
  // GlyphIDs in GDEF table.
  if (!file->maxp) {
    return OTS_FAILURE();
  }
  const uint16_t num_glyphs = file->maxp->num_glyphs;

  Buffer table(data, length);

  OpenTypeGDEF *gdef = new OpenTypeGDEF;
  file->gdef = gdef;

  uint32_t version = 0;
  if (!table.ReadU32(&version)) {
    return OTS_FAILURE();
  }
  if (version < 0x00010000 || version == 0x00010001) {
    OTS_WARNING("bad GDEF version");
    DROP_THIS_TABLE;
    return true;
  }

  if (version >= 0x00010002) {
    gdef->version_2 = true;
  }

  uint16_t offset_glyph_class_def = 0;
  uint16_t offset_attach_list = 0;
  uint16_t offset_lig_caret_list = 0;
  uint16_t offset_mark_attach_class_def = 0;
  if (!table.ReadU16(&offset_glyph_class_def) ||
      !table.ReadU16(&offset_attach_list) ||
      !table.ReadU16(&offset_lig_caret_list) ||
      !table.ReadU16(&offset_mark_attach_class_def)) {
    return OTS_FAILURE();
  }
  uint16_t offset_mark_glyph_sets_def = 0;
  if (gdef->version_2) {
    if (!table.ReadU16(&offset_mark_glyph_sets_def)) {
      return OTS_FAILURE();
    }
  }

  const unsigned gdef_header_end = static_cast<unsigned>(8) +
      gdef->version_2 ? static_cast<unsigned>(2) : static_cast<unsigned>(0);
  // Parse subtables
  if (offset_glyph_class_def) {
    if (offset_glyph_class_def >= length ||
        offset_glyph_class_def < gdef_header_end) {
      return OTS_FAILURE();
    }
    if (!ParseGlyphClassDefTable(file, data + offset_glyph_class_def,
                                 length - offset_glyph_class_def,
                                 num_glyphs)) {
      DROP_THIS_TABLE;
      return true;
    }
  }

  if (offset_attach_list) {
    if (offset_attach_list >= length ||
        offset_attach_list < gdef_header_end) {
      return OTS_FAILURE();
    }
    if (!ParseAttachListTable(file, data + offset_attach_list,
                              length - offset_attach_list,
                              num_glyphs)) {
      DROP_THIS_TABLE;
      return true;
    }
  }

  if (offset_lig_caret_list) {
    if (offset_lig_caret_list >= length ||
        offset_lig_caret_list < gdef_header_end) {
      return OTS_FAILURE();
    }
    if (!ParseLigCaretListTable(file, data + offset_lig_caret_list,
                              length - offset_lig_caret_list,
                              num_glyphs)) {
      DROP_THIS_TABLE;
      return true;
    }
  }

  if (offset_mark_attach_class_def) {
    if (offset_mark_attach_class_def >= length ||
        offset_mark_attach_class_def < gdef_header_end) {
      return OTS_FAILURE();
    }
    if (!ParseMarkAttachClassDefTable(file,
                                      data + offset_mark_attach_class_def,
                                      length - offset_mark_attach_class_def,
                                      num_glyphs)) {
      DROP_THIS_TABLE;
      return true;
    }
  }

  if (offset_mark_glyph_sets_def) {
    if (offset_mark_glyph_sets_def >= length ||
        offset_mark_glyph_sets_def < gdef_header_end) {
      return OTS_FAILURE();
    }
    if (!ParseMarkGlyphSetsDefTable(file,
                                    data + offset_mark_glyph_sets_def,
                                    length - offset_mark_glyph_sets_def,
                                    num_glyphs)) {
      DROP_THIS_TABLE;
      return true;
    }
  }
  gdef->data = data;
  gdef->length = length;
  return true;
}

bool ots_gdef_should_serialise(OpenTypeFile *file) {
  return file->gdef;
}

bool ots_gdef_serialise(OTSStream *out, OpenTypeFile *file) {
  if (!out->Write(file->gdef->data, file->gdef->length)) {
    return OTS_FAILURE();
  }

  return true;
}

void ots_gdef_free(OpenTypeFile *file) {
  delete file->gdef;
}

}  // namespace ots

<commit_msg>Fix MSVC compiler warning.<commit_after>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "gdef.h"

#include <vector>

#include "layout.h"
#include "maxp.h"

// GDEF - The Glyph Definition Table
// http://www.microsoft.com/typography/otspec/gdef.htm

namespace {

// The maximum class value in class definition tables.
const uint16_t kMaxClassDefValue = 0xFFFF;
// The maximum class value in the glyph class definision table.
const uint16_t kMaxGlyphClassDefValue = 4;
// The maximum format number of caret value tables.
// We don't support format 3 for now. See the comment in
// ParseLigCaretListTable() for the reason.
const uint16_t kMaxCaretValueFormat = 2;

bool ParseGlyphClassDefTable(ots::OpenTypeFile *file, const uint8_t *data,
                             size_t length, const uint16_t num_glyphs) {
  return ots::ParseClassDefTable(data, length, num_glyphs,
                                 kMaxGlyphClassDefValue);
}

bool ParseAttachListTable(ots::OpenTypeFile *file, const uint8_t *data,
                          size_t length, const uint16_t num_glyphs) {
  ots::Buffer subtable(data, length);

  uint16_t offset_coverage = 0;
  uint16_t glyph_count = 0;
  if (!subtable.ReadU16(&offset_coverage) ||
      !subtable.ReadU16(&glyph_count)) {
    return OTS_FAILURE();
  }
  const unsigned attach_points_end = static_cast<unsigned>(4) + 2*glyph_count;
  if (offset_coverage == 0 || offset_coverage >= length ||
      offset_coverage < attach_points_end) {
    return OTS_FAILURE();
  }
  if (glyph_count > num_glyphs) {
    OTS_WARNING("bad glyph count: %u", glyph_count);
    return OTS_FAILURE();
  }

  std::vector<uint16_t> attach_points;
  attach_points.resize(glyph_count);
  for (unsigned i = 0; i < glyph_count; ++i) {
    if (!subtable.ReadU16(&attach_points[i])) {
      return OTS_FAILURE();
    }
    if (attach_points[i] >= length ||
        attach_points[i] < attach_points_end) {
      return OTS_FAILURE();
    }
  }

  // Parse coverage table
  if (!ots::ParseCoverageTable(data + offset_coverage,
                               length - offset_coverage, num_glyphs)) {
    return OTS_FAILURE();
  }

  // Parse attach point table
  for (unsigned i = 0; i < attach_points.size(); ++i) {
    subtable.set_offset(attach_points[i]);
    uint16_t point_count = 0;
    if (!subtable.ReadU16(&point_count)) {
      return OTS_FAILURE();
    }
    if (point_count == 0) {
      return OTS_FAILURE();
    }
    uint16_t last_point_index = 0;
    uint16_t point_index = 0;
    for (unsigned j = 0; j < point_count; ++j) {
      if (!subtable.ReadU16(&point_index)) {
        return OTS_FAILURE();
      }
      // Contour point indeces are in increasing numerical order
      if (last_point_index != 0 && last_point_index >= point_index) {
        OTS_WARNING("bad contour indeces: %u >= %u",
                    last_point_index, point_index);
        return OTS_FAILURE();
      }
      last_point_index = point_index;
    }
  }
  return true;
}

bool ParseLigCaretListTable(ots::OpenTypeFile *file, const uint8_t *data,
                            size_t length, const uint16_t num_glyphs) {
  ots::Buffer subtable(data, length);
  uint16_t offset_coverage = 0;
  uint16_t lig_glyph_count = 0;
  if (!subtable.ReadU16(&offset_coverage) ||
      !subtable.ReadU16(&lig_glyph_count)) {
    return OTS_FAILURE();
  }
  const unsigned lig_glyphs_end = static_cast<unsigned>(4) + 2*lig_glyph_count;
  if (offset_coverage == 0 || offset_coverage >= length ||
      offset_coverage < lig_glyphs_end) {
    return OTS_FAILURE();
  }
  if (lig_glyph_count > num_glyphs) {
    OTS_WARNING("bad ligature glyph count: %u", lig_glyph_count);
    return OTS_FAILURE();
  }

  std::vector<uint16_t> lig_glyphs;
  lig_glyphs.resize(lig_glyph_count);
  for (unsigned i = 0; i < lig_glyph_count; ++i) {
    if (!subtable.ReadU16(&lig_glyphs[i])) {
      return OTS_FAILURE();
    }
    if (lig_glyphs[i] >= length || lig_glyphs[i] < lig_glyphs_end) {
      return OTS_FAILURE();
    }
  }

  // Parse coverage table
  if (!ots::ParseCoverageTable(data + offset_coverage,
                               length - offset_coverage, num_glyphs)) {
    return OTS_FAILURE();
  }

  // Parse ligature glyph table
  for (unsigned i = 0; i < lig_glyphs.size(); ++i) {
    subtable.set_offset(lig_glyphs[i]);
    uint16_t caret_count = 0;
    if (!subtable.ReadU16(&caret_count)) {
      return OTS_FAILURE();
    }
    if (caret_count == 0) {
      OTS_WARNING("bad caret value count: %u", caret_count);
      return OTS_FAILURE();
    }

    std::vector<uint16_t> caret_values;
    caret_values.resize(caret_count);
    uint16_t last_offset_caret = 0;
    unsigned caret_values_end = static_cast<unsigned>(2) * 2*caret_count;
    for (unsigned j = 0; j < caret_count; ++j) {
      if (!subtable.ReadU16(&caret_values[j])) {
        return OTS_FAILURE();
      }
      if (caret_values[j] >= length || caret_values[j] < caret_values_end) {
        return OTS_FAILURE();
      }
      // Caret offsets are in increasing coordinate order
      if (last_offset_caret != 0 && last_offset_caret >= caret_values[j]) {
        OTS_WARNING("offset isn't in increasing coordinate order: %u >= %u",
                    last_offset_caret, caret_values[j]);
        return OTS_FAILURE();
      }
      last_offset_caret = caret_values[j];
    }

    // Parse caret values table
    for (unsigned j = 0; j < caret_count; ++j) {
      subtable.set_offset(lig_glyphs[i] + caret_values[j]);
      uint16_t caret_format = 0;
      if (!subtable.ReadU16(&caret_format)) {
        return OTS_FAILURE();
      }
      // TODO(bashi): We only support caret value format 1 and 2 for now
      // because there are no fonts which contain caret value format 3
      // as far as we investigated.
      if (caret_format == 0 || caret_format > kMaxCaretValueFormat) {
        OTS_WARNING("bad caret value format: %u", caret_format);
        return OTS_FAILURE();
      }
      // CaretValueFormats contain a 2-byte field which could be
      // arbitrary value.
      if (!subtable.Skip(2)) {
        return OTS_FAILURE();
      }
    }
  }
  return true;
}

bool ParseMarkAttachClassDefTable(ots::OpenTypeFile *file, const uint8_t *data,
                                  size_t length, const uint16_t num_glyphs) {
  return ots::ParseClassDefTable(data, length, num_glyphs, kMaxClassDefValue);
}

bool ParseMarkGlyphSetsDefTable(ots::OpenTypeFile *file, const uint8_t *data,
                                size_t length, const uint16_t num_glyphs) {
  ots::Buffer subtable(data, length);
  uint16_t format = 0;
  uint16_t mark_set_count = 0;
  if (!subtable.ReadU16(&format) ||
      !subtable.ReadU16(&mark_set_count)) {
    return OTS_FAILURE();
  }
  if (format != 1) {
    OTS_WARNING("bad mark glyph set table format: %u", format);
    return OTS_FAILURE();
  }

  const unsigned mark_sets_end = static_cast<unsigned>(4) + 2*mark_set_count;
  for (unsigned i = 0; i < mark_set_count; ++i) {
    uint32_t offset_coverage = 0;
    if (!subtable.ReadU32(&offset_coverage)) {
      return OTS_FAILURE();
    }
    if (offset_coverage >= length ||
        offset_coverage < mark_sets_end) {
      return OTS_FAILURE();
    }
    if (!ots::ParseCoverageTable(data + offset_coverage,
                                 length - offset_coverage, num_glyphs)) {
      return OTS_FAILURE();
    }
  }
  return true;
}

}  // namespace

#define DROP_THIS_TABLE \
  do { delete file->gdef; file->gdef = 0; } while (0)

namespace ots {

bool ots_gdef_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
  // Grab the number of glyphs in the file from the maxp table to check
  // GlyphIDs in GDEF table.
  if (!file->maxp) {
    return OTS_FAILURE();
  }
  const uint16_t num_glyphs = file->maxp->num_glyphs;

  Buffer table(data, length);

  OpenTypeGDEF *gdef = new OpenTypeGDEF;
  file->gdef = gdef;

  uint32_t version = 0;
  if (!table.ReadU32(&version)) {
    return OTS_FAILURE();
  }
  if (version < 0x00010000 || version == 0x00010001) {
    OTS_WARNING("bad GDEF version");
    DROP_THIS_TABLE;
    return true;
  }

  if (version >= 0x00010002) {
    gdef->version_2 = true;
  }

  uint16_t offset_glyph_class_def = 0;
  uint16_t offset_attach_list = 0;
  uint16_t offset_lig_caret_list = 0;
  uint16_t offset_mark_attach_class_def = 0;
  if (!table.ReadU16(&offset_glyph_class_def) ||
      !table.ReadU16(&offset_attach_list) ||
      !table.ReadU16(&offset_lig_caret_list) ||
      !table.ReadU16(&offset_mark_attach_class_def)) {
    return OTS_FAILURE();
  }
  uint16_t offset_mark_glyph_sets_def = 0;
  if (gdef->version_2) {
    if (!table.ReadU16(&offset_mark_glyph_sets_def)) {
      return OTS_FAILURE();
    }
  }

  const unsigned gdef_header_end = static_cast<unsigned>(8) +
      gdef->version_2 ? static_cast<unsigned>(2) : static_cast<unsigned>(0);
  // Parse subtables
  if (offset_glyph_class_def) {
    if (offset_glyph_class_def >= length ||
        offset_glyph_class_def < gdef_header_end) {
      return OTS_FAILURE();
    }
    if (!ParseGlyphClassDefTable(file, data + offset_glyph_class_def,
                                 length - offset_glyph_class_def,
                                 num_glyphs)) {
      DROP_THIS_TABLE;
      return true;
    }
  }

  if (offset_attach_list) {
    if (offset_attach_list >= length ||
        offset_attach_list < gdef_header_end) {
      return OTS_FAILURE();
    }
    if (!ParseAttachListTable(file, data + offset_attach_list,
                              length - offset_attach_list,
                              num_glyphs)) {
      DROP_THIS_TABLE;
      return true;
    }
  }

  if (offset_lig_caret_list) {
    if (offset_lig_caret_list >= length ||
        offset_lig_caret_list < gdef_header_end) {
      return OTS_FAILURE();
    }
    if (!ParseLigCaretListTable(file, data + offset_lig_caret_list,
                              length - offset_lig_caret_list,
                              num_glyphs)) {
      DROP_THIS_TABLE;
      return true;
    }
  }

  if (offset_mark_attach_class_def) {
    if (offset_mark_attach_class_def >= length ||
        offset_mark_attach_class_def < gdef_header_end) {
      return OTS_FAILURE();
    }
    if (!ParseMarkAttachClassDefTable(file,
                                      data + offset_mark_attach_class_def,
                                      length - offset_mark_attach_class_def,
                                      num_glyphs)) {
      DROP_THIS_TABLE;
      return true;
    }
  }

  if (offset_mark_glyph_sets_def) {
    if (offset_mark_glyph_sets_def >= length ||
        offset_mark_glyph_sets_def < gdef_header_end) {
      return OTS_FAILURE();
    }
    if (!ParseMarkGlyphSetsDefTable(file,
                                    data + offset_mark_glyph_sets_def,
                                    length - offset_mark_glyph_sets_def,
                                    num_glyphs)) {
      DROP_THIS_TABLE;
      return true;
    }
  }
  gdef->data = data;
  gdef->length = length;
  return true;
}

bool ots_gdef_should_serialise(OpenTypeFile *file) {
  return file->gdef != NULL;
}

bool ots_gdef_serialise(OTSStream *out, OpenTypeFile *file) {
  if (!out->Write(file->gdef->data, file->gdef->length)) {
    return OTS_FAILURE();
  }

  return true;
}

void ots_gdef_free(OpenTypeFile *file) {
  delete file->gdef;
}

}  // namespace ots

<|endoftext|>


<commit_before>/*
 * Redistribution and use in source and binary forms, with or
 * without modification, are permitted provided that the following
 * conditions are met:
 *
 * 1. Redistributions of source code must retain the above
 *    copyright notice, this list of conditions and the
 *    following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above
 *    copyright notice, this list of conditions and the following
 *    disclaimer in the documentation and/or other materials
 *    provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
 * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */
#include "evio.h"
#include <stdio.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>

#if EV_MULTIPLICITY
#error libev with enabled EV_MULTIPLICITY is not supported yet
#endif

#define BIND_RETRY_DELAY 0.1

/**
 * Try to convert IPv4 or IPv6 addresses from text to binary form.
 * sa buf must be sizeo of sizeof(sockaddr_in6).
 */
int
evio_pton(const char *addr, const char *port, struct sockaddr_storage *sa, socklen_t *salen) {
	struct sockaddr_in *v4 = (struct sockaddr_in*)sa;
	int rc = inet_pton(AF_INET, addr, &v4->sin_addr);
	if (rc) {
		v4->sin_family = AF_INET;
		v4->sin_port = htons(atoi(port));
		*salen = sizeof(struct sockaddr_in);
		return AF_INET;
	}
	struct sockaddr_in6 *v6 = (struct sockaddr_in6*)sa;
	rc = inet_pton(AF_INET6, addr, &v6->sin6_addr);
	if (rc) {
		v6->sin6_family = AF_INET6;
		v6->sin6_port = htons(atoi(port));
		*salen = sizeof(struct sockaddr_in6);
		return AF_INET6;
	}
	return -1;
}

/** Note: this function does not throw. */
void
evio_close(struct ev_io *evio)
{
	/* Stop I/O events. Safe to do even if not started. */
	ev_io_stop(evio);
	/* Close the socket. */
	close(evio->fd);
	/* Make sure evio_is_active() returns a proper value. */
	evio->fd = -1;
}

/**
 * Create an endpoint for communication.
 * Set socket as non-block and apply protocol specific options.
 */
void
evio_socket(struct ev_io *coio, int domain, int type, int protocol)
{
	assert(coio->fd == -1);
	/* Don't leak fd if setsockopt fails. */
	coio->fd = sio_socket(domain, type, protocol);
	if (type == SOCK_STREAM) {
		evio_setsockopt_tcp(coio->fd);
	} else {
		sio_setfl(coio->fd, O_NONBLOCK, 1);
	}
}

/** Set common tcp socket client options. */
void
evio_setsockopt_tcp(int fd)
{
	int on = 1;
	/* In case this throws, the socket is not leaked. */
	sio_setfl(fd, O_NONBLOCK, on);
	/* SO_KEEPALIVE to ensure connections don't hang
	 * around for too long when a link goes away.
	 */
	sio_setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE,
		       &on, sizeof(on));
	/*
	 * Lower latency is more important than higher
	 * bandwidth, and we usually write entire
	 * request/response in a single syscall.
	 */
	sio_setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,
		       &on, sizeof(on));
}

/** Set tcp options for server sockets. */
void
evio_setsockopt_tcpserver(int fd)
{
	int on = 1;
	/* In case this throws, the socket is not leaked. */
	sio_setfl(fd, O_NONBLOCK, on);
	/* Allow reuse local adresses. */
	sio_setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
		       &on, sizeof(on));
	sio_setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE,
		       &on, sizeof(on));

	/* Send all buffered messages on socket before take
	 * control out from close(2) or shutdown(2). */
	struct linger linger = { 0, 0 };

	sio_setsockopt(fd, SOL_SOCKET, SO_LINGER,
		       &linger, sizeof(linger));
}

/**
 * Bind to a first address in addrinfo list and initialize coio
 * with bound socket.
 */
void
evio_bind_addrinfo(struct ev_io *evio, struct addrinfo *ai)
{
	assert(! evio_is_active(evio));
	int fd = -1;
	while (ai) {
		struct sockaddr_in *addr = (struct sockaddr_in *)ai->ai_addr;
		try {
			fd = sio_socket(ai->ai_family, ai->ai_socktype,
					ai->ai_protocol);
			evio_setsockopt_tcpserver(fd);
			if (sio_bind(fd, addr, ai->ai_addrlen) == 0) {
				evio->fd = fd;
				return; /* success. */
			}
			assert(errno == EADDRINUSE);
		} catch (const SocketError& e) {
			if (ai->ai_next == NULL) {
				close(fd);
				throw;
			}
		}
		close(fd);
		ai = ai->ai_next;
	}
	tnt_raise(SocketError, evio->fd, "evio_bind_addrinfo()");
}

static inline int
evio_service_port(struct evio_service *service)
{
	return ntohs(service->addr.sin_port);
}

/**
 * A callback invoked by libev when acceptor socket is ready.
 * Accept the socket, initialize it and pass to the on_accept
 * callback.
 */
static void
evio_service_accept_cb(ev_io *watcher,
		       int revents __attribute__((unused)))
{
	struct evio_service *service = (struct evio_service *) watcher->data;
	int fd = -1;

	try {
		struct sockaddr_in addr;
		socklen_t addrlen = sizeof(addr);
		fd = sio_accept(service->ev.fd, &addr, &addrlen);

		if (fd < 0) /* EAGAIN, EWOULDLOCK, EINTR */
			return;
		/* set common tcp options */
		evio_setsockopt_tcp(fd);
		/*
		 * Invoke the callback and pass it the accepted
		 * socket.
		 */
		service->on_accept(service, fd, &addr);

	} catch (const Exception& e) {
		if (fd >= 0)
			close(fd);
		e.log();
	}
}

/** Try to bind and listen on the configured port.
 *
 * Throws an exception if error.
 * Returns -1 if the address is already in use, and one
 * needs to retry binding.
 */
static int
evio_service_bind_and_listen(struct evio_service *service)
{
	/* Create a socket. */
	int fd = sio_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

	try {
		evio_setsockopt_tcpserver(fd);

		if (sio_bind(fd, &service->addr, sizeof(service->addr)) ||
		    sio_listen(fd)) {
			assert(errno == EADDRINUSE);
			close(fd);
			return -1;
		}
		say_info("bound to port %i", evio_service_port(service));

		/* Invoke on_bind callback if it is set. */
		if (service->on_bind)
			service->on_bind(service->on_bind_param);

	} catch (const Exception& e) {
		close(fd);
		throw;
	}
	/* Register the socket in the event loop. */
	ev_io_set(&service->ev, fd, EV_READ);
	ev_io_start(&service->ev);
	return 0;
}

/** A callback invoked by libev when sleep timer expires.
 *
 * Retry binding. On success, stop the timer. If the port
 * is still in use, pause again.
 */
static void
evio_service_timer_cb(ev_timer *watcher, int revents __attribute__((unused)))
{
	struct evio_service *service = (struct evio_service *) watcher->data;
	assert(! ev_is_active(&service->ev));

	if (evio_service_bind_and_listen(service) == 0)
		ev_timer_stop(watcher);
}

void
evio_service_init(struct evio_service *service, const char *name,
		  const char *host, int port,
		  void (*on_accept)(struct evio_service *, int,
				    struct sockaddr_in *),
		  void *on_accept_param)
{
	memset(service, 0, sizeof(struct evio_service));
	snprintf(service->name, sizeof(service->name), "%s", name);

	service->addr.sin_family = AF_INET;
	service->addr.sin_port = htons(port);
	if (strcmp(host, "INADDR_ANY") == 0) {
		service->addr.sin_addr.s_addr = INADDR_ANY;
	} else if (inet_aton(host, &service->addr.sin_addr) == 0) {
		tnt_raise(SocketError, -1, "invalid address for bind: %s",
			  host);
	}
	service->on_accept = on_accept;
	service->on_accept_param = on_accept_param;
	/*
	 * Initialize libev objects to be able to detect if they
	 * are active or not in evio_service_stop().
	 */
	ev_init(&service->ev, evio_service_accept_cb);
	ev_init(&service->timer, evio_service_timer_cb);
	service->timer.data = service->ev.data = service;
}

/**
 * Try to bind and listen. If the port is in use,
 * say a warning, and start the timer which will retry
 * binding periodically.
 */
void
evio_service_start(struct evio_service *service)
{
	assert(! ev_is_active(&service->ev));

	if (evio_service_bind_and_listen(service)) {
		/* Try again after a delay. */
		say_warn("port %i is already in use, will "
			 "retry binding after %lf seconds.",
			 evio_service_port(service), BIND_RETRY_DELAY);

		ev_timer_set(&service->timer,
			     BIND_RETRY_DELAY, BIND_RETRY_DELAY);
		ev_timer_start(&service->timer);
	}
}

/** It's safe to stop a service which is not started yet. */
void
evio_service_stop(struct evio_service *service)
{
	if (! ev_is_active(&service->ev)) {
		ev_timer_stop(&service->timer);
	} else {
		ev_io_stop(&service->ev);
		close(service->ev.fd);
	}
}
<commit_msg>Add port names to the server log output.<commit_after>/*
 * Redistribution and use in source and binary forms, with or
 * without modification, are permitted provided that the following
 * conditions are met:
 *
 * 1. Redistributions of source code must retain the above
 *    copyright notice, this list of conditions and the
 *    following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above
 *    copyright notice, this list of conditions and the following
 *    disclaimer in the documentation and/or other materials
 *    provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
 * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */
#include "evio.h"
#include <stdio.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>

#if EV_MULTIPLICITY
#error libev with enabled EV_MULTIPLICITY is not supported yet
#endif

#define BIND_RETRY_DELAY 0.1

/**
 * Try to convert IPv4 or IPv6 addresses from text to binary form.
 * sa buf must be sizeo of sizeof(sockaddr_in6).
 */
int
evio_pton(const char *addr, const char *port, struct sockaddr_storage *sa, socklen_t *salen) {
	struct sockaddr_in *v4 = (struct sockaddr_in*)sa;
	int rc = inet_pton(AF_INET, addr, &v4->sin_addr);
	if (rc) {
		v4->sin_family = AF_INET;
		v4->sin_port = htons(atoi(port));
		*salen = sizeof(struct sockaddr_in);
		return AF_INET;
	}
	struct sockaddr_in6 *v6 = (struct sockaddr_in6*)sa;
	rc = inet_pton(AF_INET6, addr, &v6->sin6_addr);
	if (rc) {
		v6->sin6_family = AF_INET6;
		v6->sin6_port = htons(atoi(port));
		*salen = sizeof(struct sockaddr_in6);
		return AF_INET6;
	}
	return -1;
}

/** Note: this function does not throw. */
void
evio_close(struct ev_io *evio)
{
	/* Stop I/O events. Safe to do even if not started. */
	ev_io_stop(evio);
	/* Close the socket. */
	close(evio->fd);
	/* Make sure evio_is_active() returns a proper value. */
	evio->fd = -1;
}

/**
 * Create an endpoint for communication.
 * Set socket as non-block and apply protocol specific options.
 */
void
evio_socket(struct ev_io *coio, int domain, int type, int protocol)
{
	assert(coio->fd == -1);
	/* Don't leak fd if setsockopt fails. */
	coio->fd = sio_socket(domain, type, protocol);
	if (type == SOCK_STREAM) {
		evio_setsockopt_tcp(coio->fd);
	} else {
		sio_setfl(coio->fd, O_NONBLOCK, 1);
	}
}

/** Set common tcp socket client options. */
void
evio_setsockopt_tcp(int fd)
{
	int on = 1;
	/* In case this throws, the socket is not leaked. */
	sio_setfl(fd, O_NONBLOCK, on);
	/* SO_KEEPALIVE to ensure connections don't hang
	 * around for too long when a link goes away.
	 */
	sio_setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE,
		       &on, sizeof(on));
	/*
	 * Lower latency is more important than higher
	 * bandwidth, and we usually write entire
	 * request/response in a single syscall.
	 */
	sio_setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,
		       &on, sizeof(on));
}

/** Set tcp options for server sockets. */
void
evio_setsockopt_tcpserver(int fd)
{
	int on = 1;
	/* In case this throws, the socket is not leaked. */
	sio_setfl(fd, O_NONBLOCK, on);
	/* Allow reuse local adresses. */
	sio_setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
		       &on, sizeof(on));
	sio_setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE,
		       &on, sizeof(on));

	/* Send all buffered messages on socket before take
	 * control out from close(2) or shutdown(2). */
	struct linger linger = { 0, 0 };

	sio_setsockopt(fd, SOL_SOCKET, SO_LINGER,
		       &linger, sizeof(linger));
}

/**
 * Bind to a first address in addrinfo list and initialize coio
 * with bound socket.
 */
void
evio_bind_addrinfo(struct ev_io *evio, struct addrinfo *ai)
{
	assert(! evio_is_active(evio));
	int fd = -1;
	while (ai) {
		struct sockaddr_in *addr = (struct sockaddr_in *)ai->ai_addr;
		try {
			fd = sio_socket(ai->ai_family, ai->ai_socktype,
					ai->ai_protocol);
			evio_setsockopt_tcpserver(fd);
			if (sio_bind(fd, addr, ai->ai_addrlen) == 0) {
				evio->fd = fd;
				return; /* success. */
			}
			assert(errno == EADDRINUSE);
		} catch (const SocketError& e) {
			if (ai->ai_next == NULL) {
				close(fd);
				throw;
			}
		}
		close(fd);
		ai = ai->ai_next;
	}
	tnt_raise(SocketError, evio->fd, "evio_bind_addrinfo()");
}

static inline int
evio_service_port(struct evio_service *service)
{
	return ntohs(service->addr.sin_port);
}

static inline const char *
evio_service_name(struct evio_service *service)
{
	return service->name;
}

/**
 * A callback invoked by libev when acceptor socket is ready.
 * Accept the socket, initialize it and pass to the on_accept
 * callback.
 */
static void
evio_service_accept_cb(ev_io *watcher,
		       int revents __attribute__((unused)))
{
	struct evio_service *service = (struct evio_service *) watcher->data;
	int fd = -1;

	try {
		struct sockaddr_in addr;
		socklen_t addrlen = sizeof(addr);
		fd = sio_accept(service->ev.fd, &addr, &addrlen);

		if (fd < 0) /* EAGAIN, EWOULDLOCK, EINTR */
			return;
		/* set common tcp options */
		evio_setsockopt_tcp(fd);
		/*
		 * Invoke the callback and pass it the accepted
		 * socket.
		 */
		service->on_accept(service, fd, &addr);

	} catch (const Exception& e) {
		if (fd >= 0)
			close(fd);
		e.log();
	}
}

/** Try to bind and listen on the configured port.
 *
 * Throws an exception if error.
 * Returns -1 if the address is already in use, and one
 * needs to retry binding.
 */
static int
evio_service_bind_and_listen(struct evio_service *service)
{
	/* Create a socket. */
	int fd = sio_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

	try {
		evio_setsockopt_tcpserver(fd);

		if (sio_bind(fd, &service->addr, sizeof(service->addr)) ||
		    sio_listen(fd)) {
			assert(errno == EADDRINUSE);
			close(fd);
			return -1;
		}
		say_info("bound to %s port %i", evio_service_name(service),
			 evio_service_port(service));

		/* Invoke on_bind callback if it is set. */
		if (service->on_bind)
			service->on_bind(service->on_bind_param);

	} catch (const Exception& e) {
		close(fd);
		throw;
	}
	/* Register the socket in the event loop. */
	ev_io_set(&service->ev, fd, EV_READ);
	ev_io_start(&service->ev);
	return 0;
}

/** A callback invoked by libev when sleep timer expires.
 *
 * Retry binding. On success, stop the timer. If the port
 * is still in use, pause again.
 */
static void
evio_service_timer_cb(ev_timer *watcher, int revents __attribute__((unused)))
{
	struct evio_service *service = (struct evio_service *) watcher->data;
	assert(! ev_is_active(&service->ev));

	if (evio_service_bind_and_listen(service) == 0)
		ev_timer_stop(watcher);
}

void
evio_service_init(struct evio_service *service, const char *name,
		  const char *host, int port,
		  void (*on_accept)(struct evio_service *, int,
				    struct sockaddr_in *),
		  void *on_accept_param)
{
	memset(service, 0, sizeof(struct evio_service));
	snprintf(service->name, sizeof(service->name), "%s", name);

	service->addr.sin_family = AF_INET;
	service->addr.sin_port = htons(port);
	if (strcmp(host, "INADDR_ANY") == 0) {
		service->addr.sin_addr.s_addr = INADDR_ANY;
	} else if (inet_aton(host, &service->addr.sin_addr) == 0) {
		tnt_raise(SocketError, -1, "invalid address for bind: %s",
			  host);
	}
	service->on_accept = on_accept;
	service->on_accept_param = on_accept_param;
	/*
	 * Initialize libev objects to be able to detect if they
	 * are active or not in evio_service_stop().
	 */
	ev_init(&service->ev, evio_service_accept_cb);
	ev_init(&service->timer, evio_service_timer_cb);
	service->timer.data = service->ev.data = service;
}

/**
 * Try to bind and listen. If the port is in use,
 * say a warning, and start the timer which will retry
 * binding periodically.
 */
void
evio_service_start(struct evio_service *service)
{
	assert(! ev_is_active(&service->ev));

	if (evio_service_bind_and_listen(service)) {
		/* Try again after a delay. */
		say_warn("%s port %i is already in use, will "
			 "retry binding after %lf seconds.",
			 evio_service_name(service),
			 evio_service_port(service), BIND_RETRY_DELAY);

		ev_timer_set(&service->timer,
			     BIND_RETRY_DELAY, BIND_RETRY_DELAY);
		ev_timer_start(&service->timer);
	}
}

/** It's safe to stop a service which is not started yet. */
void
evio_service_stop(struct evio_service *service)
{
	if (! ev_is_active(&service->ev)) {
		ev_timer_stop(&service->timer);
	} else {
		ev_io_stop(&service->ev);
		close(service->ev.fd);
	}
}
<|endoftext|>


<commit_before>#include <iostream>
#include <regex>
#include <boost/filesystem.hpp>

namespace fs = boost::filesystem;

static const std::string ANSI_PURPLE = "\x1b[35;06m";
static const std::string ANSI_CYAN = "\x1b[36;01m";
static const std::string ANSI_RESET = "\x1b[0m";

void printPath(const fs::path& path) {
    if (fs::is_symlink(path)) {
        std::cout << ANSI_PURPLE;
    } else if (fs::is_directory(path)) {
        std::cout << ANSI_CYAN;
    }

    std::cout << path.string();

    std::cout << ANSI_RESET << std::endl;

}

void findFiles(const std::regex& pattern) {
    const fs::path& currentPath = fs::current_path();

    for (auto& entry: fs::recursive_directory_iterator(currentPath)) {
        const fs::path& path = entry.path().lexically_relative(currentPath);

        if (std::regex_search(path.string(), pattern)) {
            printPath(path);
        }
    }
}

int main(int argc, char* argv[]) {
    std::string argument;

    if (argc == 1) {
        argument = "";
    } else if (argc == 2) {
        argument = argv[1];
    }

    if (argc > 2 || argument == "-h" || argument == "--help") {
        std::cerr << "Usage: fnd [PATTERN]" << std::endl;
        return 1;
    }

    // try to parse the argument as a regex
    try {
        std::regex re(argument);

        findFiles(re);
    }
    catch (const std::regex_error& e) {
        std::cerr << "Regex error: " << e.what() << std::endl;
        return 1;
    }

    return 0;
}
<commit_msg>Case insensitivity, closes #3<commit_after>#include <iostream>
#include <regex>
#include <boost/filesystem.hpp>

namespace fs = boost::filesystem;

static const std::string ANSI_PURPLE = "\x1b[35;06m";
static const std::string ANSI_CYAN = "\x1b[36;01m";
static const std::string ANSI_RESET = "\x1b[0m";

void printPath(const fs::path& path) {
    if (fs::is_symlink(path)) {
        std::cout << ANSI_PURPLE;
    } else if (fs::is_directory(path)) {
        std::cout << ANSI_CYAN;
    }

    std::cout << path.string();

    std::cout << ANSI_RESET << std::endl;

}

void findFiles(const std::regex& pattern) {
    const fs::path& currentPath = fs::current_path();

    for (auto& entry: fs::recursive_directory_iterator(currentPath)) {
        const fs::path& path = entry.path().lexically_relative(currentPath);

        if (std::regex_search(path.string(), pattern)) {
            printPath(path);
        }
    }
}

int main(int argc, char* argv[]) {
    std::string argument;

    if (argc == 1) {
        argument = "";
    } else if (argc == 2) {
        argument = argv[1];
    }

    if (argc > 2 || argument == "-h" || argument == "--help") {
        std::cerr << "Usage: fnd [PATTERN]" << std::endl;
        return 1;
    }

    // try to parse the argument as a regex
    try {
        std::regex re(argument, std::regex_constants::ECMAScript
                              | std::regex_constants::icase      );

        findFiles(re);
    }
    catch (const std::regex_error& e) {
        std::cerr << "Regex error: " << e.what() << std::endl;
        return 1;
    }

    return 0;
}
<|endoftext|>


<commit_before>#include "painter/painter.hpp"
#include "painter/brush.hpp"
#include "painter/color.hpp"
#include "painter/glyph.hpp"
#include "painter/glyph_string.hpp"
#include "painter/paint_buffer.hpp"
#include "system/system.hpp"
#include "widget/border.hpp"
#include "widget/coordinates.hpp"
#include "widget/widget.hpp"

#include <optional/optional.hpp>

#include <cstddef>
#include <cstring>

namespace cppurses {

Painter::Painter(Widget* widget) : widget_{widget} {}

void Painter::put(const Glyph_string& text, std::size_t x, std::size_t y) {
    if (!widget_->on_tree() || !widget_->visible()) {
        return;
    }
    Coordinates original_position{widget_->cursor_x(), widget_->cursor_y()};
    move_cursor(*widget_, x, y);
    for (Glyph g : text) {
        add_default_attributes(&g);
        auto glob_x = widget_->x() + widget_->cursor_x();
        auto glob_y = widget_->y() + widget_->cursor_y();
        if (std::strcmp(g.c_str(), "\n") == 0) {
            move_cursor(*widget_, 0, widget_->cursor_y() + 1);
        }  // TODO else if ( == \t) then move to next x coord divisible by
           // tabspace
           // should be here and textbox should just account for it.
        else {
            System::paint_buffer()->stage(glob_x, glob_y, g);
            move_cursor(*widget_, widget_->cursor_x() + 1, widget_->cursor_y());
        }
    }
    if (!move_cursor_on_put) {
        move_cursor(*widget_, original_position.x, original_position.y);
    }
}

void Painter::put(const Glyph_string& text, Coordinates position) {
    this->put(text, position.x, position.y);
}

void Painter::put(const Glyph_string& text) {
    this->put(text, widget_->cursor_x(), widget_->cursor_y());
}

void Painter::fill(std::size_t x,
                   std::size_t y,
                   std::size_t width,
                   std::size_t height,
                   const Glyph& tile) {
    if (width == 0) {
        return;
    }
    for (; y < height; ++y) {
        this->line(x, y, width - 1, y, tile);
    }
}

void Painter::line(std::size_t x1,
                   std::size_t y1,
                   std::size_t x2,
                   std::size_t y2,
                   const Glyph_string& gs) {
    // No diagonal lines atm.
    if (y1 == y2) {  // Horizontal
        for (; x1 <= x2; ++x1) {
            this->put(gs, x1, y1);
        }
    } else if (x1 == x2) {  // Vertical
        for (; y1 <= y2; ++y1) {
            this->put(gs, x1, y1);
        }
    }
}

void Painter::border(const Border& b) {
    if (!b.enabled) {
        return;
    }
    std::size_t width = widget_->width() + west_border_offset(*widget_);
    std::size_t height = widget_->height() + north_border_offset(*widget_);
    if (width < 1 || height < 1) {
        return;
    }

    const std::size_t widg_x = widget_->x() - west_border_offset(*widget_);
    const std::size_t widg_y = widget_->y() - north_border_offset(*widget_);

    // Underflow Checks
    if (widg_x + width < 1 && (b.north_enabled || b.south_enabled)) {
        return;
    }
    if (widg_y + height < 1 && (b.east_enabled || b.west_enabled)) {
        return;
    }

    // North Wall
    Coordinates north_left{widg_x + 1, widg_y};
    Coordinates north_right{widg_x + width - 1, widg_y};
    // South Wall
    Coordinates south_left{widg_x + 1, widg_y + height};
    Coordinates south_right{widg_x + width - 1, widg_y + height};
    // West Wall
    Coordinates west_top{widg_x, widg_y + 1};
    Coordinates west_bottom{widg_x, widg_y + height - 1};
    // East Wall
    Coordinates east_top{widg_x + width, widg_y + 1};
    Coordinates east_bottom{widg_x + width, widg_y + height - 1};

    // Corners
    Coordinates north_east{east_top.x, north_left.y};
    Coordinates north_west{west_top.x, north_right.y};
    Coordinates south_east{east_bottom.x, south_left.y};
    Coordinates south_west{west_bottom.x, south_right.y};

    // Edge Cases:
    // Height == 1
    if (widget_->height() == 1 && widget_->north_border_disqualified()) {
        west_top = Coordinates{widg_x, widg_y};
        west_bottom = west_top;
        east_top = Coordinates{widg_x + width, widg_y};
        east_bottom = east_top;
    } else if (widget_->height() == 1 && widget_->south_border_disqualified() &&
               !widget_->north_border_disqualified()) {  // && b.north_enabled?
        west_top = Coordinates{widg_x, widg_y + 1};
        west_bottom = west_top;
        east_top = Coordinates{widg_x + width, widg_y + 1};
        east_bottom = east_top;
    }

    // Width == 1
    if (widget_->width() == 1 && widget_->west_border_disqualified()) {
        north_left = Coordinates{widg_x, widg_y};
        north_right = north_left;
        south_left = Coordinates{widg_x, widg_y + height /*- 1*/};
        south_right = south_left;
    } else if (widget_->width() == 1 && widget_->east_border_disqualified() &&
               !widget_->west_border_disqualified()) {  // && b.west_enabled?
        north_left = Coordinates{widg_x + 1, widg_y};
        north_right = north_left;
        south_left = Coordinates{widg_x + 1, widg_y + height /*- 1*/};
        south_right = south_left;
    }

    // North
    if (b.north_enabled && !widget_->north_border_disqualified()) {
        this->unbound_line(north_left, north_right, b.north);
    }
    // South
    if (b.south_enabled && !widget_->south_border_disqualified()) {
        this->unbound_line(south_left, south_right, b.south);
    }
    // West
    if (b.west_enabled && !widget_->west_border_disqualified()) {
        this->unbound_line(west_top, west_bottom, b.west);
    }
    // East
    if (b.east_enabled && !widget_->east_border_disqualified()) {
        this->unbound_line(east_top, east_bottom, b.east);
    }
    // North-West
    if (b.north_west_enabled && !widget_->north_border_disqualified() &&
        !widget_->west_border_disqualified()) {
        this->unbound_put_string(north_west, b.north_west);
    }
    // North-East
    if (b.north_east_enabled && !widget_->north_border_disqualified() &&
        !widget_->east_border_disqualified()) {
        this->unbound_put_string(north_east, b.north_east);
    }
    // South-West
    if (b.south_west_enabled && !widget_->south_border_disqualified() &&
        !widget_->west_border_disqualified()) {
        this->unbound_put_string(south_west, b.south_west);
    }
    // South-East
    if (b.south_east_enabled && !widget_->south_border_disqualified() &&
        !widget_->east_border_disqualified()) {
        this->unbound_put_string(south_east, b.south_east);
    }

    // Corners - Special Cases
    // North-West
    if (!b.north_west_enabled && !b.north_enabled && b.west_enabled) {
        this->unbound_put_string(north_west, b.west);
    } else if (!b.north_west_enabled && !b.west_enabled && b.north_enabled) {
        this->unbound_put_string(north_west, b.north);
    }
    // North-East
    if (!b.north_east_enabled && !b.north_enabled && b.east_enabled) {
        this->unbound_put_string(north_east, b.east);
    } else if (!b.north_east_enabled && !b.east_enabled && b.north_enabled) {
        this->unbound_put_string(north_east, b.north);
    }
    // South-West
    if (!b.south_west_enabled && !b.south_enabled && b.west_enabled) {
        this->unbound_put_string(south_west, b.west);
    } else if (!b.south_west_enabled && !b.west_enabled && b.south_enabled) {
        this->unbound_put_string(south_west, b.south);
    }
    // South-East
    if (!b.south_east_enabled && !b.south_enabled && b.east_enabled) {
        this->unbound_put_string(south_east, b.east);
    } else if (!b.south_east_enabled && !b.east_enabled && b.south_enabled) {
        this->unbound_put_string(south_east, b.south);
    }
}

void Painter::clear_screen() {
    auto width = widget_->width() + west_border_offset(*widget_) +
                 east_border_offset(*widget_);
    auto height = widget_->height() + north_border_offset(*widget_) +
                  south_border_offset(*widget_);
    auto gx = widget_->x() - west_border_offset(*widget_);
    auto gy = widget_->y() - north_border_offset(*widget_);

    if (width == 0 || height == 0) {
        return;
    }
    Glyph bg_tile = widget_->background_tile;
    if (!bg_tile.brush().background_color() &&
        widget_->brush.background_color()) {
        bg_tile.brush().set_background(*widget_->brush.background_color());
    }
    if (!bg_tile.brush().foreground_color() &&
        widget_->brush.foreground_color()) {
        bg_tile.brush().set_foreground(*widget_->brush.foreground_color());
    }
    for (std::size_t i{gy}; i < gy + height; ++i) {
        this->unbound_line(gx, i, gx + width - 1, i, bg_tile);
    }
}

void Painter::unbound_put_string(const Coordinates& point,
                                 const Glyph_string& gs) {
    this->unbound_put_string(point.x, point.y, gs);
}

void Painter::unbound_put_string(std::size_t glob_x,
                                 std::size_t glob_y,
                                 const Glyph_string& gs) {
    if (!widget_->on_tree() || !widget_->visible()) {
        return;
    }
    for (Glyph g : gs) {
        add_default_attributes(&g);
        System::paint_buffer()->stage(glob_x++, glob_y, g);
    }
}

void Painter::unbound_line(const Coordinates& point_1,
                           const Coordinates& point_2,
                           const Glyph& symbol) {
    this->unbound_line(point_1.x, point_1.y, point_2.x, point_2.y, symbol);
}

void Painter::unbound_line(std::size_t glob_x1,
                           std::size_t glob_y1,
                           std::size_t glob_x2,
                           std::size_t glob_y2,
                           const Glyph& symbol) {
    // Horizontal
    if (glob_y1 == glob_y2) {
        for (; glob_x1 <= glob_x2; ++glob_x1) {
            unbound_put_string(glob_x1, glob_y1, symbol);
        }
    } else if (glob_x1 == glob_x2) {
        for (; glob_y1 <= glob_y2; ++glob_y1) {
            unbound_put_string(glob_x1, glob_y1, symbol);
        }
    }
}

void Painter::add_default_attributes(Glyph* g) {
    if (!g->brush().background_color() && widget_->brush.background_color()) {
        g->brush().add_attributes(
            background(*widget_->brush.background_color()));
    }
    if (!g->brush().foreground_color() && widget_->brush.foreground_color()) {
        g->brush().add_attributes(
            foreground(*widget_->brush.foreground_color()));
    }
    for (const auto& attr : widget_->brush.attributes()) {
        g->brush().add_attributes(attr);
    }
}

}  // namespace cppurses
<commit_msg>Fix Border printing bug.<commit_after>#include "painter/painter.hpp"
#include "painter/brush.hpp"
#include "painter/color.hpp"
#include "painter/glyph.hpp"
#include "painter/glyph_string.hpp"
#include "painter/paint_buffer.hpp"
#include "system/system.hpp"
#include "widget/border.hpp"
#include "widget/coordinates.hpp"
#include "widget/widget.hpp"

#include <optional/optional.hpp>

#include <cstddef>
#include <cstring>

namespace cppurses {

Painter::Painter(Widget* widget) : widget_{widget} {}

void Painter::put(const Glyph_string& text, std::size_t x, std::size_t y) {
    if (!widget_->on_tree() || !widget_->visible()) {
        return;
    }
    Coordinates original_position{widget_->cursor_x(), widget_->cursor_y()};
    move_cursor(*widget_, x, y);
    for (Glyph g : text) {
        add_default_attributes(&g);
        auto glob_x = widget_->x() + widget_->cursor_x();
        auto glob_y = widget_->y() + widget_->cursor_y();
        if (std::strcmp(g.c_str(), "\n") == 0) {
            move_cursor(*widget_, 0, widget_->cursor_y() + 1);
        }  // TODO else if ( == \t) then move to next x coord divisible by
           // tabspace
           // should be here and textbox should just account for it.
        else {
            System::paint_buffer()->stage(glob_x, glob_y, g);
            move_cursor(*widget_, widget_->cursor_x() + 1, widget_->cursor_y());
        }
    }
    if (!move_cursor_on_put) {
        move_cursor(*widget_, original_position.x, original_position.y);
    }
}

void Painter::put(const Glyph_string& text, Coordinates position) {
    this->put(text, position.x, position.y);
}

void Painter::put(const Glyph_string& text) {
    this->put(text, widget_->cursor_x(), widget_->cursor_y());
}

void Painter::fill(std::size_t x,
                   std::size_t y,
                   std::size_t width,
                   std::size_t height,
                   const Glyph& tile) {
    if (width == 0) {
        return;
    }
    for (; y < height; ++y) {
        this->line(x, y, width - 1, y, tile);
    }
}

void Painter::line(std::size_t x1,
                   std::size_t y1,
                   std::size_t x2,
                   std::size_t y2,
                   const Glyph_string& gs) {
    // No diagonal lines atm.
    if (y1 == y2) {  // Horizontal
        for (; x1 <= x2; ++x1) {
            this->put(gs, x1, y1);
        }
    } else if (x1 == x2) {  // Vertical
        for (; y1 <= y2; ++y1) {
            this->put(gs, x1, y1);
        }
    }
}

void Painter::border(const Border& b) {
    if (!b.enabled) {
        return;
    }
    std::size_t width = widget_->width() + west_border_offset(*widget_);
    std::size_t height = widget_->height() + north_border_offset(*widget_);
    if (width < 1 || height < 1) {
        return;
    }

    const std::size_t widg_x = widget_->x() - west_border_offset(*widget_);
    const std::size_t widg_y = widget_->y() - north_border_offset(*widget_);

    // Underflow Checks
    if (widg_x + width < 1 && (b.north_enabled || b.south_enabled)) {
        return;
    }
    if (widg_y + height < 1 && (b.east_enabled || b.west_enabled)) {
        return;
    }

    // North Wall
    Coordinates north_left{widg_x + 1, widg_y};
    Coordinates north_right{widg_x + width - 1, widg_y};
    // South Wall
    Coordinates south_left{widg_x + 1, widg_y + height};
    Coordinates south_right{widg_x + width - 1, widg_y + height};
    // West Wall
    Coordinates west_top{widg_x, widg_y + 1};
    Coordinates west_bottom{widg_x, widg_y + height - 1};
    // East Wall
    Coordinates east_top{widg_x + width, widg_y + 1};
    Coordinates east_bottom{widg_x + width, widg_y + height - 1};

    // Corners
    Coordinates north_east{east_top.x, north_left.y};
    Coordinates north_west{west_top.x, north_right.y};
    Coordinates south_east{east_bottom.x, south_left.y};
    Coordinates south_west{west_bottom.x, south_right.y};

    // Edge Cases:
    // Height == 1
    if (widget_->height() == 1 && widget_->north_border_disqualified()) {
        west_top = Coordinates{widg_x, widg_y};
        west_bottom = west_top;
        east_top = Coordinates{widg_x + width, widg_y};
        east_bottom = east_top;
    } else if (widget_->height() == 1 && widget_->south_border_disqualified() &&
               !widget_->north_border_disqualified()) {  // && b.north_enabled?
        west_top = Coordinates{widg_x, widg_y + 1};
        west_bottom = west_top;
        east_top = Coordinates{widg_x + width, widg_y + 1};
        east_bottom = east_top;
    }

    // Width == 1
    if (widget_->width() == 1 && widget_->west_border_disqualified()) {
        north_left = Coordinates{widg_x, widg_y};
        north_right = north_left;
        south_left = Coordinates{widg_x, widg_y + height /*- 1*/};
        south_right = south_left;
    } else if (widget_->width() == 1 && widget_->east_border_disqualified() &&
               !widget_->west_border_disqualified()) {  // && b.west_enabled?
        north_left = Coordinates{widg_x + 1, widg_y};
        north_right = north_left;
        south_left = Coordinates{widg_x + 1, widg_y + height /*- 1*/};
        south_right = south_left;
    }

    // North
    if (b.north_enabled && !widget_->north_border_disqualified()) {
        this->unbound_line(north_left, north_right, b.north);
    }
    // South
    if (b.south_enabled && !widget_->south_border_disqualified()) {
        this->unbound_line(south_left, south_right, b.south);
    }
    // West
    if (b.west_enabled && !widget_->west_border_disqualified()) {
        this->unbound_line(west_top, west_bottom, b.west);
    }
    // East
    if (b.east_enabled && !widget_->east_border_disqualified()) {
        this->unbound_line(east_top, east_bottom, b.east);
    }
    // North-West
    if (b.north_west_enabled && !widget_->north_border_disqualified() &&
        !widget_->west_border_disqualified()) {
        this->unbound_put_string(north_west, b.north_west);
    }
    // North-East
    if (b.north_east_enabled && !widget_->north_border_disqualified() &&
        !widget_->east_border_disqualified()) {
        this->unbound_put_string(north_east, b.north_east);
    }
    // South-West
    if (b.south_west_enabled && !widget_->south_border_disqualified() &&
        !widget_->west_border_disqualified()) {
        this->unbound_put_string(south_west, b.south_west);
    }
    // South-East
    if (b.south_east_enabled && !widget_->south_border_disqualified() &&
        !widget_->east_border_disqualified()) {
        this->unbound_put_string(south_east, b.south_east);
    }

    if (widget_->height() == 1 && widget_->north_border_disqualified() &&
        widget_->south_border_disqualified()) {
        return;
    }
    if (widget_->width() == 1 && widget_->west_border_disqualified() &&
        widget_->east_border_disqualified()) {
        return;
    }

    // Corners - Special Cases
    // North-West
    if (!b.north_west_enabled && !b.north_enabled && b.west_enabled) {
        this->unbound_put_string(north_west, b.west);
    } else if (!b.north_west_enabled && !b.west_enabled && b.north_enabled) {
        this->unbound_put_string(north_west, b.north);
    }
    // North-East
    if (!b.north_east_enabled && !b.north_enabled && b.east_enabled) {
        this->unbound_put_string(north_east, b.east);
    } else if (!b.north_east_enabled && !b.east_enabled && b.north_enabled) {
        this->unbound_put_string(north_east, b.north);
    }
    // South-West
    if (!b.south_west_enabled && !b.south_enabled && b.west_enabled) {
        this->unbound_put_string(south_west, b.west);
    } else if (!b.south_west_enabled && !b.west_enabled && b.south_enabled) {
        this->unbound_put_string(south_west, b.south);
    }
    // South-East
    if (!b.south_east_enabled && !b.south_enabled && b.east_enabled) {
        this->unbound_put_string(south_east, b.east);
    } else if (!b.south_east_enabled && !b.east_enabled && b.south_enabled) {
        this->unbound_put_string(south_east, b.south);
    }
}

void Painter::clear_screen() {
    auto width = widget_->width() + west_border_offset(*widget_) +
                 east_border_offset(*widget_);
    auto height = widget_->height() + north_border_offset(*widget_) +
                  south_border_offset(*widget_);
    auto gx = widget_->x() - west_border_offset(*widget_);
    auto gy = widget_->y() - north_border_offset(*widget_);

    if (width == 0 || height == 0) {
        return;
    }
    Glyph bg_tile = widget_->background_tile;
    if (!bg_tile.brush().background_color() &&
        widget_->brush.background_color()) {
        bg_tile.brush().set_background(*widget_->brush.background_color());
    }
    if (!bg_tile.brush().foreground_color() &&
        widget_->brush.foreground_color()) {
        bg_tile.brush().set_foreground(*widget_->brush.foreground_color());
    }
    for (std::size_t i{gy}; i < gy + height; ++i) {
        this->unbound_line(gx, i, gx + width - 1, i, bg_tile);
    }
}

void Painter::unbound_put_string(const Coordinates& point,
                                 const Glyph_string& gs) {
    this->unbound_put_string(point.x, point.y, gs);
}

void Painter::unbound_put_string(std::size_t glob_x,
                                 std::size_t glob_y,
                                 const Glyph_string& gs) {
    if (!widget_->on_tree() || !widget_->visible()) {
        return;
    }
    for (Glyph g : gs) {
        add_default_attributes(&g);
        System::paint_buffer()->stage(glob_x++, glob_y, g);
    }
}

void Painter::unbound_line(const Coordinates& point_1,
                           const Coordinates& point_2,
                           const Glyph& symbol) {
    this->unbound_line(point_1.x, point_1.y, point_2.x, point_2.y, symbol);
}

void Painter::unbound_line(std::size_t glob_x1,
                           std::size_t glob_y1,
                           std::size_t glob_x2,
                           std::size_t glob_y2,
                           const Glyph& symbol) {
    // Horizontal
    if (glob_y1 == glob_y2) {
        for (; glob_x1 <= glob_x2; ++glob_x1) {
            unbound_put_string(glob_x1, glob_y1, symbol);
        }
    } else if (glob_x1 == glob_x2) {
        for (; glob_y1 <= glob_y2; ++glob_y1) {
            unbound_put_string(glob_x1, glob_y1, symbol);
        }
    }
}

void Painter::add_default_attributes(Glyph* g) {
    if (!g->brush().background_color() && widget_->brush.background_color()) {
        g->brush().add_attributes(
            background(*widget_->brush.background_color()));
    }
    if (!g->brush().foreground_color() && widget_->brush.foreground_color()) {
        g->brush().add_attributes(
            foreground(*widget_->brush.foreground_color()));
    }
    for (const auto& attr : widget_->brush.attributes()) {
        g->brush().add_attributes(attr);
    }
}

}  // namespace cppurses
<|endoftext|>