Skip to content

Instantly share code, notes, and snippets.

@kolodziej
Last active August 29, 2015 14:10
Show Gist options
  • Save kolodziej/4a2e3e39acfcffe8ac44 to your computer and use it in GitHub Desktop.
Save kolodziej/4a2e3e39acfcffe8ac44 to your computer and use it in GitHub Desktop.
Shell
#include <iostream>
#include "shell_application.hpp"
class ConcatApplication :
public ShellApplication
{
public:
ConcatApplication() :
ShellApplication("Concatenate strings")
{}
int runApplication(std::vector<std::string>& argv)
{
for (auto it = argv.begin() + 1; it != argv.end(); ++it)
{
std::cout << *it;
}
std::cout << "\n";
return 0;
}
};
extern "C" const char* getName()
{
return "concat";
}
extern "C" ShellApplication* load()
{
return new ConcatApplication();
}
#include <iostream>
#include "shell_application.hpp"
class EchoApplication :
public ShellApplication
{
public:
EchoApplication() :
ShellApplication("Echo")
{}
int runApplication(std::vector<std::string>& argv)
{
for (auto it = argv.begin() + 1; it != argv.end(); ++it)
{
std::cout << *it << " ";
}
std::cout << "\n";
return 0;
}
};
extern "C" const char* getName()
{
return "echo";
}
extern "C" ShellApplication* load()
{
return new EchoApplication();
}
CXX=g++
CXXFLAGS=-std=c++11 -g -DDEBUG -Wall
APPSFLAGS=-fPIC -shared
OBJS=shell_main.o shell.o
APPS=concat_application.so echo_application.so
LIBS=-ldl
TARGET=shell
all: $(OBJS) $(APPS)
$(CXX) $(CXXFLAGS) $(OBJS) $(LIBS) -o $(TARGET)
$(APPS): %so: %cpp
$(CXX) $(CXXFLAGS) $(APPSFLAGS) $< -o $@
$(OBJS): %.o: %.cpp
$(CXX) $(CXXFLAGS) -c $< -o $@
cleanup:
rm -f $(OBJS) $(TARGET) $(APPS)
#include "shell.hpp"
#include <dlfcn.h>
#include <iostream>
#include <sstream>
#include <utility>
#include "shell_application.hpp"
Shell::Shell(std::string prompt, std::string load_name) :
prompt_(prompt),
load_name_(load_name)
{
available_apps_.insert(std::make_pair(load_name_, nullptr));
}
Shell::~Shell()
{
for (auto handler : apps_handlers_)
{
dlclose(handler);
}
apps_handlers_.clear();
}
bool Shell::loadApplication(std::string path)
{
void* handler = dlopen(path.data(), RTLD_LAZY);
if (handler == 0)
{
std::cerr << "dl library error: " << dlerror();
return false;
}
apps_handlers_.push_back(handler);
GetNamePtr getName = reinterpret_cast<GetNamePtr>(dlsym(handler, "getName"));
LoadPtr load = reinterpret_cast<LoadPtr>(dlsym(handler, "load"));
if (!(getName && load))
{
std::cerr << "dl library error: " << dlerror();
return false;
}
auto insertion = available_apps_.insert(std::make_pair(getName(), load));
if (insertion.second)
{
std::clog << getName() << " application loaded!\n";
}
return insertion.second;
}
int Shell::runCommand(std::string cmd)
{
std::vector<std::string> cmd_parts = parseCommand_(cmd);
if (cmd_parts.size() == 0)
{
std::cerr << "invalid command: " << cmd << "\n";
return -1;
}
std::string& app_name = cmd_parts[0];
if (app_name == load_name_)
{
std::cout << "Loading apps...\n";
int result = 0;
for (auto it = cmd_parts.begin() + 1; it != cmd_parts.end(); ++it)
{
std::cout << " + " << *it << " ";
if (loadApplication(std::string("./") + *it + std::string("_application.so")))
{
std::cout << "[ OK ]\n";
} else
{
std::cout << "\n";
result = 1;
}
}
return result;
}
auto app_load = available_apps_.find(app_name);
if (app_load == available_apps_.end())
{
std::cerr << "could not find application: `" << app_name << "`\n";
return -1;
}
ShellApplication* app = reinterpret_cast<ShellApplication*>(app_load->second());
int result = app->runApplication(cmd_parts);
delete app;
return result;
}
int Shell::loop()
{
std::string cmd;
std::cout << prompt_;
while (std::getline(std::cin, cmd))
{
int status = runCommand(cmd);
std::cout << "Exit code: " << status << "\n" << prompt_;
}
return 0;
}
std::vector<std::string> Shell::parseCommand_(std::string cmd)
{
std::vector<std::string> tokens;
std::stringstream token;
const char delimiter = ' ', quote = '\"', escape = '\\';
bool escapeNext = false;
bool quoteMode = false;
for (std::string::iterator it = cmd.begin(); it != cmd.end(); ++it)
{
char c = *it;
if (c == escape && escapeNext == false)
{
escapeNext = true;
} else if (c == quote && escapeNext == false)
{
quoteMode = !quoteMode;
} else if (c == delimiter && escapeNext == false && quoteMode == false)
{
if (token.str().empty() == false)
{
tokens.push_back(token.str());
token.str(std::string());
}
} else
{
token << c;
escapeNext = false;
}
}
if (token.str().empty() == false)
{
tokens.push_back(token.str());
}
return tokens;
}
#ifndef SHELL_HPP
#define SHELL_HPP
#include <string>
#include <vector>
#include <map>
class ShellApplication;
class Shell
{
typedef const char* (*GetNamePtr)();
typedef ShellApplication* (*LoadPtr)();
private:
const std::string prompt_;
const std::string load_name_;
std::map<std::string, LoadPtr> available_apps_;
std::vector<void*> apps_handlers_;
public:
Shell(std::string = std::string("shell> "), std::string = std::string(":load"));
~Shell();
bool loadApplication(std::string);
int runCommand(std::string);
int loop();
private:
std::vector<std::string> parseCommand_(std::string);
};
#endif
#ifndef SHELL_APPLICATION_HPP
#define SHELL_APPLICATION_HPP
#include <string>
#include <vector>
class ShellApplication
{
private:
std::string name_;
public:
ShellApplication(std::string name) :
name_(name)
{}
virtual ~ShellApplication() {}
std::string getName()
{
return name_;
}
virtual int runApplication(std::vector<std::string>&) = 0;
};
extern "C" const char* getName();
extern "C" ShellApplication* load();
#endif
#include <iostream>
#include <string>
#include "shell.hpp"
#include "shell_application.hpp"
int main(int argc, char **argv)
{
if (argc != 2)
{
std::cerr << "Usage: " << argv[0] << " prompt\n";
return 1;
}
Shell shell(argv[1]);
return shell.loop();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment