mirror of
https://github.com/amyinspace/MagicSetEditor2.git
synced 2026-06-11 13:17:00 -04:00
Change tabs to two spaces.
This commit is contained in:
+186
-186
@@ -20,227 +20,227 @@ DECLARE_TYPEOF_COLLECTION(ScriptParseError);
|
||||
// ----------------------------------------------------------------------------- : Command line interface
|
||||
|
||||
CLISetInterface::CLISetInterface(const SetP& set, bool quiet)
|
||||
: quiet(quiet)
|
||||
, our_context(nullptr)
|
||||
: quiet(quiet)
|
||||
, our_context(nullptr)
|
||||
{
|
||||
if (!cli.haveConsole()) {
|
||||
throw Error(_("Can not run command line interface without a console;\nstart MSE with \"mse.com --cli\""));
|
||||
}
|
||||
ei.allow_writes_outside = true;
|
||||
setExportInfoCwd();
|
||||
setSet(set);
|
||||
run();
|
||||
if (!cli.haveConsole()) {
|
||||
throw Error(_("Can not run command line interface without a console;\nstart MSE with \"mse.com --cli\""));
|
||||
}
|
||||
ei.allow_writes_outside = true;
|
||||
setExportInfoCwd();
|
||||
setSet(set);
|
||||
run();
|
||||
}
|
||||
|
||||
CLISetInterface::~CLISetInterface() {
|
||||
delete our_context;
|
||||
delete our_context;
|
||||
}
|
||||
|
||||
Context& CLISetInterface::getContext() {
|
||||
if (set) {
|
||||
return set->getContext();
|
||||
} else {
|
||||
if (!our_context) {
|
||||
our_context = new Context();
|
||||
init_script_functions(*our_context);
|
||||
}
|
||||
return *our_context;
|
||||
}
|
||||
if (set) {
|
||||
return set->getContext();
|
||||
} else {
|
||||
if (!our_context) {
|
||||
our_context = new Context();
|
||||
init_script_functions(*our_context);
|
||||
}
|
||||
return *our_context;
|
||||
}
|
||||
}
|
||||
|
||||
void CLISetInterface::onBeforeChangeSet() {
|
||||
if (set || our_context) {
|
||||
Context& ctx = getContext();
|
||||
ctx.closeScope(scope);
|
||||
}
|
||||
if (set || our_context) {
|
||||
Context& ctx = getContext();
|
||||
ctx.closeScope(scope);
|
||||
}
|
||||
}
|
||||
|
||||
void CLISetInterface::onChangeSet() {
|
||||
Context& ctx = getContext();
|
||||
scope = ctx.openScope();
|
||||
ei.set = set;
|
||||
Context& ctx = getContext();
|
||||
scope = ctx.openScope();
|
||||
ei.set = set;
|
||||
}
|
||||
|
||||
void CLISetInterface::setExportInfoCwd() {
|
||||
// write to the current directory
|
||||
ei.directory_relative = ei.directory_absolute = wxGetCwd();
|
||||
// read from the current directory
|
||||
ei.export_template = intrusive(new Package());
|
||||
ei.export_template->open(ei.directory_absolute, true);
|
||||
// write to the current directory
|
||||
ei.directory_relative = ei.directory_absolute = wxGetCwd();
|
||||
// read from the current directory
|
||||
ei.export_template = intrusive(new Package());
|
||||
ei.export_template->open(ei.directory_absolute, true);
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------- : Running
|
||||
|
||||
void CLISetInterface::run() {
|
||||
// show welcome logo
|
||||
if (!quiet) showWelcome();
|
||||
print_pending_errors();
|
||||
// loop
|
||||
running = true;
|
||||
while (running) {
|
||||
// show prompt
|
||||
if (!quiet) {
|
||||
cli << GRAY << _("> ") << NORMAL;
|
||||
cli.flush();
|
||||
}
|
||||
// read line from stdin
|
||||
String command = cli.getLine();
|
||||
if (command.empty() && !cli.canGetLine()) break;
|
||||
handleCommand(command);
|
||||
print_pending_errors();
|
||||
cli.flush();
|
||||
cli.flushRaw();
|
||||
}
|
||||
// show welcome logo
|
||||
if (!quiet) showWelcome();
|
||||
print_pending_errors();
|
||||
// loop
|
||||
running = true;
|
||||
while (running) {
|
||||
// show prompt
|
||||
if (!quiet) {
|
||||
cli << GRAY << _("> ") << NORMAL;
|
||||
cli.flush();
|
||||
}
|
||||
// read line from stdin
|
||||
String command = cli.getLine();
|
||||
if (command.empty() && !cli.canGetLine()) break;
|
||||
handleCommand(command);
|
||||
print_pending_errors();
|
||||
cli.flush();
|
||||
cli.flushRaw();
|
||||
}
|
||||
}
|
||||
|
||||
void CLISetInterface::showWelcome() {
|
||||
cli << _(" ___ \n")
|
||||
_(" __ __ _ ___ _ ___ _ _ _ |__ \\ \n")
|
||||
_(" | \\/ |__ _ __ _(_)__ / __|___| |_ | __|__| (_) |_ ___ _ _ ) |\n")
|
||||
_(" | |\\/| / _` / _` | / _| \\__ | -_) _| | _|/ _` | | _/ _ \\ '_| / / \n")
|
||||
_(" |_| |_\\__,_\\__, |_\\__| |___|___|\\__| |___\\__,_|_|\\__\\___/_| / /_ \n")
|
||||
_(" |___/ |____|\n\n");
|
||||
cli.flush();
|
||||
cli << _(" ___ \n")
|
||||
_(" __ __ _ ___ _ ___ _ _ _ |__ \\ \n")
|
||||
_(" | \\/ |__ _ __ _(_)__ / __|___| |_ | __|__| (_) |_ ___ _ _ ) |\n")
|
||||
_(" | |\\/| / _` / _` | / _| \\__ | -_) _| | _|/ _` | | _/ _ \\ '_| / / \n")
|
||||
_(" |_| |_\\__,_\\__, |_\\__| |___|___|\\__| |___\\__,_|_|\\__\\___/_| / /_ \n")
|
||||
_(" |___/ |____|\n\n");
|
||||
cli.flush();
|
||||
}
|
||||
|
||||
void CLISetInterface::showUsage() {
|
||||
cli << _(" Commands available from the prompt:\n\n");
|
||||
cli << _(" <expression> Execute a script expression, display the result\n");
|
||||
cli << _(" :help Show this help page.\n");
|
||||
cli << _(" :load <setfile> Load a different set file.\n");
|
||||
cli << _(" :quit Exit the MSE command line interface.\n");
|
||||
cli << _(" :reset Clear all local variable definitions.\n");
|
||||
cli << _(" :pwd Print the current working directory.\n");
|
||||
cli << _(" :cd Change the working directory.\n");
|
||||
cli << _(" :! <command> Perform a shell command.\n");
|
||||
cli << _("\n Commands can be abreviated to their first letter if there is no ambiguity.\n\n");
|
||||
cli << _(" Commands available from the prompt:\n\n");
|
||||
cli << _(" <expression> Execute a script expression, display the result\n");
|
||||
cli << _(" :help Show this help page.\n");
|
||||
cli << _(" :load <setfile> Load a different set file.\n");
|
||||
cli << _(" :quit Exit the MSE command line interface.\n");
|
||||
cli << _(" :reset Clear all local variable definitions.\n");
|
||||
cli << _(" :pwd Print the current working directory.\n");
|
||||
cli << _(" :cd Change the working directory.\n");
|
||||
cli << _(" :! <command> Perform a shell command.\n");
|
||||
cli << _("\n Commands can be abreviated to their first letter if there is no ambiguity.\n\n");
|
||||
}
|
||||
|
||||
void CLISetInterface::handleCommand(const String& command) {
|
||||
try {
|
||||
if (command.empty()) {
|
||||
// empty, ignore
|
||||
} else if (command.GetChar(0) == _(':')) {
|
||||
// :something
|
||||
size_t space = min(command.find_first_of(_(' ')), command.size());
|
||||
String before = command.substr(0,space);
|
||||
String arg = space + 1 < command.size() ? command.substr(space+1) : wxEmptyString;
|
||||
if (before == _(":q") || before == _(":quit")) {
|
||||
if (!quiet) {
|
||||
cli << _("Goodbye\n");
|
||||
}
|
||||
running = false;
|
||||
} else if (before == _(":?") || before == _(":h") || before == _(":help")) {
|
||||
showUsage();
|
||||
} else if (before == _(":l") || before == _(":load")) {
|
||||
if (arg.empty()) {
|
||||
cli.show_message(MESSAGE_ERROR,_("Give a filename to open."));
|
||||
} else {
|
||||
setSet(import_set(arg));
|
||||
}
|
||||
} else if (before == _(":r") || before == _(":reset")) {
|
||||
Context& ctx = getContext();
|
||||
ei.exported_images.clear();
|
||||
ctx.closeScope(scope);
|
||||
scope = ctx.openScope();
|
||||
} else if (before == _(":i") || before == _(":info")) {
|
||||
if (set) {
|
||||
cli << _("set: ") << set->identification() << ENDL;
|
||||
cli << _("filename: ") << set->absoluteFilename() << ENDL;
|
||||
cli << _("relative: ") << set->relativeFilename() << ENDL;
|
||||
cli << String::Format(_("#cards: %d"), set->cards.size()) << ENDL;
|
||||
} else {
|
||||
cli << _("No set loaded") << ENDL;
|
||||
}
|
||||
} else if (before == _(":c") || before == _(":cd")) {
|
||||
if (arg.empty()) {
|
||||
cli.show_message(MESSAGE_ERROR,_("Give a new working directory."));
|
||||
} else {
|
||||
if (!wxSetWorkingDirectory(arg)) {
|
||||
cli.show_message(MESSAGE_ERROR,_("Can't change working directory to ")+arg);
|
||||
} else {
|
||||
setExportInfoCwd();
|
||||
}
|
||||
}
|
||||
} else if (before == _(":pwd") || before == _(":p")) {
|
||||
cli << ei.directory_absolute << ENDL;
|
||||
} else if (before == _(":!")) {
|
||||
if (arg.empty()) {
|
||||
cli.show_message(MESSAGE_ERROR,_("Give a shell command to execute."));
|
||||
} else {
|
||||
#ifdef UNICODE
|
||||
#ifdef __WXMSW__
|
||||
_wsystem(arg.c_str()); // TODO: is this function available on other platforms?
|
||||
#else
|
||||
wxCharBuffer buf = arg.fn_str();
|
||||
system(buf);
|
||||
#endif
|
||||
#else
|
||||
system(arg.c_str());
|
||||
#endif
|
||||
}
|
||||
#if USE_SCRIPT_PROFILING
|
||||
} else if (before == _(":profile")) {
|
||||
if (arg == _("full")) {
|
||||
showProfilingStats(profile_root);
|
||||
} else {
|
||||
long level = 1;
|
||||
arg.ToLong(&level);
|
||||
showProfilingStats(profile_aggregated(level));
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
cli.show_message(MESSAGE_ERROR,_("Unknown command, type :help for help."));
|
||||
}
|
||||
} else if (command == _("exit") || command == _("quit")) {
|
||||
cli << _("Use :quit to quit\n");
|
||||
} else if (command == _("help")) {
|
||||
cli << _("Use :help for help\n");
|
||||
} else {
|
||||
// parse command
|
||||
vector<ScriptParseError> errors;
|
||||
ScriptP script = parse(command,nullptr,false,errors);
|
||||
if (!errors.empty()) {
|
||||
FOR_EACH(error,errors) cli.show_message(MESSAGE_ERROR,error.what());
|
||||
return;
|
||||
}
|
||||
// execute command
|
||||
WITH_DYNAMIC_ARG(export_info, &ei);
|
||||
Context& ctx = getContext();
|
||||
ScriptValueP result = ctx.eval(*script,false);
|
||||
// show result
|
||||
cli << result->toCode() << ENDL;
|
||||
}
|
||||
} catch (const Error& e) {
|
||||
cli.show_message(MESSAGE_ERROR,e.what());
|
||||
}
|
||||
try {
|
||||
if (command.empty()) {
|
||||
// empty, ignore
|
||||
} else if (command.GetChar(0) == _(':')) {
|
||||
// :something
|
||||
size_t space = min(command.find_first_of(_(' ')), command.size());
|
||||
String before = command.substr(0,space);
|
||||
String arg = space + 1 < command.size() ? command.substr(space+1) : wxEmptyString;
|
||||
if (before == _(":q") || before == _(":quit")) {
|
||||
if (!quiet) {
|
||||
cli << _("Goodbye\n");
|
||||
}
|
||||
running = false;
|
||||
} else if (before == _(":?") || before == _(":h") || before == _(":help")) {
|
||||
showUsage();
|
||||
} else if (before == _(":l") || before == _(":load")) {
|
||||
if (arg.empty()) {
|
||||
cli.show_message(MESSAGE_ERROR,_("Give a filename to open."));
|
||||
} else {
|
||||
setSet(import_set(arg));
|
||||
}
|
||||
} else if (before == _(":r") || before == _(":reset")) {
|
||||
Context& ctx = getContext();
|
||||
ei.exported_images.clear();
|
||||
ctx.closeScope(scope);
|
||||
scope = ctx.openScope();
|
||||
} else if (before == _(":i") || before == _(":info")) {
|
||||
if (set) {
|
||||
cli << _("set: ") << set->identification() << ENDL;
|
||||
cli << _("filename: ") << set->absoluteFilename() << ENDL;
|
||||
cli << _("relative: ") << set->relativeFilename() << ENDL;
|
||||
cli << String::Format(_("#cards: %d"), set->cards.size()) << ENDL;
|
||||
} else {
|
||||
cli << _("No set loaded") << ENDL;
|
||||
}
|
||||
} else if (before == _(":c") || before == _(":cd")) {
|
||||
if (arg.empty()) {
|
||||
cli.show_message(MESSAGE_ERROR,_("Give a new working directory."));
|
||||
} else {
|
||||
if (!wxSetWorkingDirectory(arg)) {
|
||||
cli.show_message(MESSAGE_ERROR,_("Can't change working directory to ")+arg);
|
||||
} else {
|
||||
setExportInfoCwd();
|
||||
}
|
||||
}
|
||||
} else if (before == _(":pwd") || before == _(":p")) {
|
||||
cli << ei.directory_absolute << ENDL;
|
||||
} else if (before == _(":!")) {
|
||||
if (arg.empty()) {
|
||||
cli.show_message(MESSAGE_ERROR,_("Give a shell command to execute."));
|
||||
} else {
|
||||
#ifdef UNICODE
|
||||
#ifdef __WXMSW__
|
||||
_wsystem(arg.c_str()); // TODO: is this function available on other platforms?
|
||||
#else
|
||||
wxCharBuffer buf = arg.fn_str();
|
||||
system(buf);
|
||||
#endif
|
||||
#else
|
||||
system(arg.c_str());
|
||||
#endif
|
||||
}
|
||||
#if USE_SCRIPT_PROFILING
|
||||
} else if (before == _(":profile")) {
|
||||
if (arg == _("full")) {
|
||||
showProfilingStats(profile_root);
|
||||
} else {
|
||||
long level = 1;
|
||||
arg.ToLong(&level);
|
||||
showProfilingStats(profile_aggregated(level));
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
cli.show_message(MESSAGE_ERROR,_("Unknown command, type :help for help."));
|
||||
}
|
||||
} else if (command == _("exit") || command == _("quit")) {
|
||||
cli << _("Use :quit to quit\n");
|
||||
} else if (command == _("help")) {
|
||||
cli << _("Use :help for help\n");
|
||||
} else {
|
||||
// parse command
|
||||
vector<ScriptParseError> errors;
|
||||
ScriptP script = parse(command,nullptr,false,errors);
|
||||
if (!errors.empty()) {
|
||||
FOR_EACH(error,errors) cli.show_message(MESSAGE_ERROR,error.what());
|
||||
return;
|
||||
}
|
||||
// execute command
|
||||
WITH_DYNAMIC_ARG(export_info, &ei);
|
||||
Context& ctx = getContext();
|
||||
ScriptValueP result = ctx.eval(*script,false);
|
||||
// show result
|
||||
cli << result->toCode() << ENDL;
|
||||
}
|
||||
} catch (const Error& e) {
|
||||
cli.show_message(MESSAGE_ERROR,e.what());
|
||||
}
|
||||
}
|
||||
|
||||
#if USE_SCRIPT_PROFILING
|
||||
DECLARE_TYPEOF_COLLECTION(FunctionProfileP);
|
||||
void CLISetInterface::showProfilingStats(const FunctionProfile& item, int level) {
|
||||
// show parent
|
||||
if (level == 0) {
|
||||
cli << GRAY << _("Time(s) Avg (ms) Calls Function") << ENDL;
|
||||
cli << _("======== ======== ====== ===============================") << NORMAL << ENDL;
|
||||
} else {
|
||||
for (int i = 1 ; i < level ; ++i) cli << _(" ");
|
||||
cli << String::Format(_("%8.5f %8.5f %6d %s"), item.total_time(), 1000 * item.avg_time(), item.calls, item.name.c_str()) << ENDL;
|
||||
}
|
||||
// show children
|
||||
vector<FunctionProfileP> children;
|
||||
item.get_children(children);
|
||||
FOR_EACH_REVERSE(c, children) {
|
||||
showProfilingStats(*c, level + 1);
|
||||
}
|
||||
}
|
||||
DECLARE_TYPEOF_COLLECTION(FunctionProfileP);
|
||||
void CLISetInterface::showProfilingStats(const FunctionProfile& item, int level) {
|
||||
// show parent
|
||||
if (level == 0) {
|
||||
cli << GRAY << _("Time(s) Avg (ms) Calls Function") << ENDL;
|
||||
cli << _("======== ======== ====== ===============================") << NORMAL << ENDL;
|
||||
} else {
|
||||
for (int i = 1 ; i < level ; ++i) cli << _(" ");
|
||||
cli << String::Format(_("%8.5f %8.5f %6d %s"), item.total_time(), 1000 * item.avg_time(), item.calls, item.name.c_str()) << ENDL;
|
||||
}
|
||||
// show children
|
||||
vector<FunctionProfileP> children;
|
||||
item.get_children(children);
|
||||
FOR_EACH_REVERSE(c, children) {
|
||||
showProfilingStats(*c, level + 1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void CLISetInterface::print_pending_errors() {
|
||||
MessageType type;
|
||||
String msg;
|
||||
while (get_queued_message(type,msg)) {
|
||||
cli.show_message(type,msg);
|
||||
}
|
||||
MessageType type;
|
||||
String msg;
|
||||
while (get_queued_message(type,msg)) {
|
||||
cli.show_message(type,msg);
|
||||
}
|
||||
}
|
||||
|
||||
+26
-26
@@ -18,34 +18,34 @@
|
||||
|
||||
class CLISetInterface : public SetView {
|
||||
public:
|
||||
/// The set is optional
|
||||
CLISetInterface(const SetP& set, bool quiet = false);
|
||||
~CLISetInterface();
|
||||
/// The set is optional
|
||||
CLISetInterface(const SetP& set, bool quiet = false);
|
||||
~CLISetInterface();
|
||||
protected:
|
||||
virtual void onAction(const Action&, bool) {}
|
||||
virtual void onChangeSet();
|
||||
virtual void onBeforeChangeSet();
|
||||
virtual void onAction(const Action&, bool) {}
|
||||
virtual void onChangeSet();
|
||||
virtual void onBeforeChangeSet();
|
||||
private:
|
||||
bool quiet; ///< Supress prompts and other non-vital stuff
|
||||
bool running; ///< Still running?
|
||||
|
||||
void run();
|
||||
void showWelcome();
|
||||
void showUsage();
|
||||
void handleCommand(const String& command);
|
||||
#if USE_SCRIPT_PROFILING
|
||||
void showProfilingStats(const FunctionProfile& parent, int level = 0);
|
||||
#endif
|
||||
void print_pending_errors();
|
||||
|
||||
/// our own context, when no set is loaded
|
||||
Context& getContext();
|
||||
Context* our_context;
|
||||
size_t scope;
|
||||
|
||||
// export info, so we can write files
|
||||
ExportInfo ei;
|
||||
void setExportInfoCwd();
|
||||
bool quiet; ///< Supress prompts and other non-vital stuff
|
||||
bool running; ///< Still running?
|
||||
|
||||
void run();
|
||||
void showWelcome();
|
||||
void showUsage();
|
||||
void handleCommand(const String& command);
|
||||
#if USE_SCRIPT_PROFILING
|
||||
void showProfilingStats(const FunctionProfile& parent, int level = 0);
|
||||
#endif
|
||||
void print_pending_errors();
|
||||
|
||||
/// our own context, when no set is loaded
|
||||
Context& getContext();
|
||||
Context* our_context;
|
||||
size_t scope;
|
||||
|
||||
// export info, so we can write files
|
||||
ExportInfo ei;
|
||||
void setExportInfoCwd();
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
|
||||
+113
-113
@@ -24,155 +24,155 @@ const Char* ENDL = _("\n");
|
||||
TextIOHandler cli;
|
||||
|
||||
#ifdef __WXMSW__
|
||||
bool StdHandleOk(DWORD std_handle) {
|
||||
// GetStdHandle sometimes returns an invalid handle instead of INVALID_HANDLE_VALUE
|
||||
// check with GetHandleInformation
|
||||
HANDLE h = GetStdHandle(std_handle);
|
||||
DWORD flags;
|
||||
return GetHandleInformation(h,&flags);
|
||||
}
|
||||
bool StdHandleOk(DWORD std_handle) {
|
||||
// GetStdHandle sometimes returns an invalid handle instead of INVALID_HANDLE_VALUE
|
||||
// check with GetHandleInformation
|
||||
HANDLE h = GetStdHandle(std_handle);
|
||||
DWORD flags;
|
||||
return GetHandleInformation(h,&flags);
|
||||
}
|
||||
#endif
|
||||
|
||||
void TextIOHandler::init() {
|
||||
bool have_stderr;
|
||||
#if defined(__WXMSW__)
|
||||
have_console = false;
|
||||
escapes = false;
|
||||
// Detect whether to use console output
|
||||
have_console = StdHandleOk(STD_OUTPUT_HANDLE);
|
||||
have_stderr = StdHandleOk(STD_ERROR_HANDLE);
|
||||
// Detect the --color flag, indicating we should allow escapes
|
||||
if (have_console) {
|
||||
for (int i = 1 ; i < wxTheApp->argc ; ++i) {
|
||||
if (String(wxTheApp->argv[i]) == _("--color")) {
|
||||
escapes = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
// TODO: detect console on linux?
|
||||
have_console = false;
|
||||
have_stderr = false;
|
||||
// Use console mode if one of the cli flags is passed
|
||||
static const Char* redirect_flags[] = {_("-?"),_("--help"),_("-v"),_("--version"),_("--cli"),_("-c"),_("--export"),_("--create-installer")};
|
||||
for (int i = 1 ; i < wxTheApp->argc ; ++i) {
|
||||
for (int j = 0 ; j < sizeof(redirect_flags)/sizeof(redirect_flags[0]) ; ++j) {
|
||||
if (String(wxTheApp->argv[i]) == redirect_flags[j]) {
|
||||
have_console = true;
|
||||
have_stderr = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
escapes = true; // TODO: detect output redirection
|
||||
#endif
|
||||
// write to standard output
|
||||
stream = stdout;
|
||||
raw_mode = false;
|
||||
// always write to stderr if possible
|
||||
if (have_console) {
|
||||
write_errors_to_cli = true;
|
||||
}
|
||||
bool have_stderr;
|
||||
#if defined(__WXMSW__)
|
||||
have_console = false;
|
||||
escapes = false;
|
||||
// Detect whether to use console output
|
||||
have_console = StdHandleOk(STD_OUTPUT_HANDLE);
|
||||
have_stderr = StdHandleOk(STD_ERROR_HANDLE);
|
||||
// Detect the --color flag, indicating we should allow escapes
|
||||
if (have_console) {
|
||||
for (int i = 1 ; i < wxTheApp->argc ; ++i) {
|
||||
if (String(wxTheApp->argv[i]) == _("--color")) {
|
||||
escapes = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
// TODO: detect console on linux?
|
||||
have_console = false;
|
||||
have_stderr = false;
|
||||
// Use console mode if one of the cli flags is passed
|
||||
static const Char* redirect_flags[] = {_("-?"),_("--help"),_("-v"),_("--version"),_("--cli"),_("-c"),_("--export"),_("--create-installer")};
|
||||
for (int i = 1 ; i < wxTheApp->argc ; ++i) {
|
||||
for (int j = 0 ; j < sizeof(redirect_flags)/sizeof(redirect_flags[0]) ; ++j) {
|
||||
if (String(wxTheApp->argv[i]) == redirect_flags[j]) {
|
||||
have_console = true;
|
||||
have_stderr = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
escapes = true; // TODO: detect output redirection
|
||||
#endif
|
||||
// write to standard output
|
||||
stream = stdout;
|
||||
raw_mode = false;
|
||||
// always write to stderr if possible
|
||||
if (have_console) {
|
||||
write_errors_to_cli = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool TextIOHandler::haveConsole() const {
|
||||
return have_console;
|
||||
return have_console;
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------- : Output
|
||||
|
||||
TextIOHandler& TextIOHandler::operator << (const Char* str) {
|
||||
if ((escapes && !raw_mode) || str[0] != 27) {
|
||||
if (have_console && !raw_mode) {
|
||||
IF_UNICODE(fwprintf,fprintf)(stream,str);
|
||||
} else {
|
||||
buffer += str;
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
if ((escapes && !raw_mode) || str[0] != 27) {
|
||||
if (have_console && !raw_mode) {
|
||||
IF_UNICODE(fwprintf,fprintf)(stream,str);
|
||||
} else {
|
||||
buffer += str;
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
TextIOHandler& TextIOHandler::operator << (const String& str) {
|
||||
return *this << static_cast<const Char*>(str.c_str());
|
||||
return *this << static_cast<const Char*>(str.c_str());
|
||||
}
|
||||
|
||||
void TextIOHandler::flush() {
|
||||
if (raw_mode) return;
|
||||
if (have_console) {
|
||||
fflush(stream);
|
||||
} else if (!buffer.empty()) {
|
||||
// Show message box
|
||||
wxMessageBox(buffer, _("Magic Set Editor"), wxOK | wxICON_INFORMATION);
|
||||
buffer.clear();
|
||||
}
|
||||
if (raw_mode) return;
|
||||
if (have_console) {
|
||||
fflush(stream);
|
||||
} else if (!buffer.empty()) {
|
||||
// Show message box
|
||||
wxMessageBox(buffer, _("Magic Set Editor"), wxOK | wxICON_INFORMATION);
|
||||
buffer.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Input
|
||||
|
||||
String TextIOHandler::getLine() {
|
||||
String result;
|
||||
Char buffer[2048];
|
||||
while (!feof(stdin)) {
|
||||
if (!IF_UNICODE(fgetws,fgets)(buffer, 2048, stdin)) {
|
||||
return result; // error
|
||||
}
|
||||
result += buffer;
|
||||
if (result.GetChar(result.size()-1) == _('\n')) {
|
||||
// drop newline, done
|
||||
result.resize(result.size() - 1);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
String result;
|
||||
Char buffer[2048];
|
||||
while (!feof(stdin)) {
|
||||
if (!IF_UNICODE(fgetws,fgets)(buffer, 2048, stdin)) {
|
||||
return result; // error
|
||||
}
|
||||
result += buffer;
|
||||
if (result.GetChar(result.size()-1) == _('\n')) {
|
||||
// drop newline, done
|
||||
result.resize(result.size() - 1);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
bool TextIOHandler::canGetLine() {
|
||||
return !feof(stdin);
|
||||
return !feof(stdin);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Raw mode
|
||||
|
||||
void TextIOHandler::enableRaw() {
|
||||
raw_mode = true;
|
||||
raw_mode_status = 0;
|
||||
raw_mode = true;
|
||||
raw_mode_status = 0;
|
||||
}
|
||||
|
||||
void TextIOHandler::flushRaw() {
|
||||
if (!raw_mode) return;
|
||||
// always end in a newline
|
||||
if (!buffer.empty() && buffer.GetChar(buffer.size()-1) != _('\n')) {
|
||||
buffer += _('\n');
|
||||
}
|
||||
// count newlines
|
||||
int newline_count = 0;
|
||||
FOR_EACH_CONST(c,buffer) if (c==_('\n')) newline_count++;
|
||||
// write record
|
||||
printf("%d\n%d\n", raw_mode_status, newline_count);
|
||||
if (!buffer.empty()) {
|
||||
#ifdef UNICODE
|
||||
wxCharBuffer buf = buffer.mb_str(wxConvUTF8);
|
||||
fputs(buf,stdout);
|
||||
#else
|
||||
fputs(buffer.c_str(),stdout);
|
||||
#endif
|
||||
}
|
||||
fflush(stdout);
|
||||
// clear
|
||||
buffer.clear();
|
||||
raw_mode_status = 0;
|
||||
if (!raw_mode) return;
|
||||
// always end in a newline
|
||||
if (!buffer.empty() && buffer.GetChar(buffer.size()-1) != _('\n')) {
|
||||
buffer += _('\n');
|
||||
}
|
||||
// count newlines
|
||||
int newline_count = 0;
|
||||
FOR_EACH_CONST(c,buffer) if (c==_('\n')) newline_count++;
|
||||
// write record
|
||||
printf("%d\n%d\n", raw_mode_status, newline_count);
|
||||
if (!buffer.empty()) {
|
||||
#ifdef UNICODE
|
||||
wxCharBuffer buf = buffer.mb_str(wxConvUTF8);
|
||||
fputs(buf,stdout);
|
||||
#else
|
||||
fputs(buffer.c_str(),stdout);
|
||||
#endif
|
||||
}
|
||||
fflush(stdout);
|
||||
// clear
|
||||
buffer.clear();
|
||||
raw_mode_status = 0;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Errors
|
||||
|
||||
void TextIOHandler::show_message(MessageType type, String const& message) {
|
||||
stream = stdout;
|
||||
if (type == MESSAGE_WARNING) {
|
||||
*this << YELLOW << _("WARNING: ") << NORMAL << replace_all(message,_("\n"),_("\n ")) << ENDL;
|
||||
} else {
|
||||
*this << RED << _("ERROR: ") << NORMAL << replace_all(message,_("\n"),_("\n ")) << ENDL;
|
||||
}
|
||||
flush();
|
||||
stream = stdout;
|
||||
if (raw_mode) raw_mode_status = max(raw_mode_status, type == MESSAGE_WARNING ? 1 : 2);
|
||||
stream = stdout;
|
||||
if (type == MESSAGE_WARNING) {
|
||||
*this << YELLOW << _("WARNING: ") << NORMAL << replace_all(message,_("\n"),_("\n ")) << ENDL;
|
||||
} else {
|
||||
*this << RED << _("ERROR: ") << NORMAL << replace_all(message,_("\n"),_("\n ")) << ENDL;
|
||||
}
|
||||
flush();
|
||||
stream = stdout;
|
||||
if (raw_mode) raw_mode_status = max(raw_mode_status, type == MESSAGE_WARNING ? 1 : 2);
|
||||
}
|
||||
|
||||
+32
-32
@@ -19,39 +19,39 @@ extern const Char *BRIGHT, *NORMAL, *PARAM, *FILE_EXT, *GRAY, *RED, *ENDL;
|
||||
/// Command line input / output handler
|
||||
class TextIOHandler {
|
||||
public:
|
||||
void init();
|
||||
|
||||
/// Do we have a console to read/write from/to?
|
||||
bool haveConsole() const;
|
||||
|
||||
/// Output text to the console
|
||||
TextIOHandler& operator << (const Char*);
|
||||
TextIOHandler& operator << (const String&);
|
||||
|
||||
/// Read a line from stdin
|
||||
String getLine();
|
||||
/// Can another line be got?
|
||||
bool canGetLine();
|
||||
|
||||
/// Flush output
|
||||
void flush();
|
||||
|
||||
/// Show an error or warning message
|
||||
void show_message(MessageType type, String const& message);
|
||||
|
||||
/// Enable raw mode
|
||||
void enableRaw();
|
||||
/// Output a single raw-mode record
|
||||
/// Has no effect unless enableRaw() was called
|
||||
void flushRaw();
|
||||
|
||||
void init();
|
||||
|
||||
/// Do we have a console to read/write from/to?
|
||||
bool haveConsole() const;
|
||||
|
||||
/// Output text to the console
|
||||
TextIOHandler& operator << (const Char*);
|
||||
TextIOHandler& operator << (const String&);
|
||||
|
||||
/// Read a line from stdin
|
||||
String getLine();
|
||||
/// Can another line be got?
|
||||
bool canGetLine();
|
||||
|
||||
/// Flush output
|
||||
void flush();
|
||||
|
||||
/// Show an error or warning message
|
||||
void show_message(MessageType type, String const& message);
|
||||
|
||||
/// Enable raw mode
|
||||
void enableRaw();
|
||||
/// Output a single raw-mode record
|
||||
/// Has no effect unless enableRaw() was called
|
||||
void flushRaw();
|
||||
|
||||
private:
|
||||
bool have_console;
|
||||
bool escapes;
|
||||
FILE* stream;
|
||||
String buffer; ///< Buffer when not writing to console
|
||||
bool raw_mode;
|
||||
int raw_mode_status;
|
||||
bool have_console;
|
||||
bool escapes;
|
||||
FILE* stream;
|
||||
String buffer; ///< Buffer when not writing to console
|
||||
bool raw_mode;
|
||||
int raw_mode_status;
|
||||
};
|
||||
|
||||
/// The global TextIOHandler object
|
||||
|
||||
+211
-211
@@ -37,8 +37,8 @@
|
||||
|
||||
/// How to transfer data from one handle to another
|
||||
struct Transfer {
|
||||
HANDLE from, &to;
|
||||
bool escapes;
|
||||
HANDLE from, &to;
|
||||
bool escapes;
|
||||
};
|
||||
|
||||
DWORD WINAPI TransferThread(Transfer*);
|
||||
@@ -60,181 +60,181 @@ HANDLE err_mine, err_theirs, err_real;
|
||||
const char* redirect_flags[] = {"-?","--help","-v","--version","--cli","-c","--export","--create-installer"};
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
// determine whether we need to wrap console i/o
|
||||
bool need_redirection = false;
|
||||
for (int i = 1 ; i < argc ; ++i) {
|
||||
for (int j = 0 ; j < sizeof(redirect_flags)/sizeof(redirect_flags[0]) ; ++j) {
|
||||
if (strcmp(argv[i],redirect_flags[j]) == 0) {
|
||||
need_redirection = true;
|
||||
goto break_2;
|
||||
}
|
||||
}
|
||||
}
|
||||
break_2:
|
||||
|
||||
// command line
|
||||
TCHAR* command_line = GetCommandLine();
|
||||
if (need_redirection) {
|
||||
// update command line : add --color flag
|
||||
TCHAR* my_command_line = command_line;
|
||||
command_line = new TCHAR[_tcsclen(command_line) + 10];
|
||||
_tcscpy(command_line, my_command_line);
|
||||
_tcscat(command_line, _T(" --color"));
|
||||
}
|
||||
|
||||
// application name
|
||||
TCHAR app_path[2048];
|
||||
GetModuleFileName(NULL/*current process*/, app_path, sizeof(app_path)/sizeof(TCHAR));
|
||||
size_t app_path_length = _tcsclen(app_path);
|
||||
if (app_path_length > 4 && _tcsicmp(app_path + app_path_length - 4, _T(".com")) == 0) {
|
||||
// replace ".com" with ".exe"
|
||||
_tcscpy(app_path + app_path_length - 4, _T(".exe"));
|
||||
} else {
|
||||
// not a .com file, error message
|
||||
fprintf(stderr, "This executable should be named <something>.com\n");
|
||||
}
|
||||
|
||||
// win32 structures for child program
|
||||
STARTUPINFO child_startup_info;
|
||||
memset(&child_startup_info, 0, sizeof(child_startup_info));
|
||||
memset(&child_process_info, 0, sizeof(child_process_info));
|
||||
child_startup_info.cb = sizeof(child_startup_info);
|
||||
|
||||
// setup redirection
|
||||
if (need_redirection) {
|
||||
// Ctrl+C handler
|
||||
SetConsoleCtrlHandler(HandleCtrlEvent, TRUE);
|
||||
|
||||
// create pipes
|
||||
CreatePipe(&in_theirs, &in_mine, NULL, 0);
|
||||
CreatePipe(&out_mine, &out_theirs, NULL, 0);
|
||||
CreatePipe(&err_mine, &err_theirs, NULL, 0);
|
||||
|
||||
// the actual handles
|
||||
in_real = GetStdHandle(STD_INPUT_HANDLE);
|
||||
out_real = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
err_real = GetStdHandle(STD_ERROR_HANDLE);
|
||||
InitEscapeTranslation(out_real);
|
||||
|
||||
// start threads
|
||||
Transfer tranfer_in = {in_real, in_mine, false};
|
||||
Transfer tranfer_out = {out_mine, out_real, true};
|
||||
Transfer tranfer_err = {err_mine, err_real, true};
|
||||
CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)TransferThread,&tranfer_in, 0,NULL);
|
||||
CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)TransferThread,&tranfer_out,0,NULL);
|
||||
CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)TransferThread,&tranfer_err,0,NULL);
|
||||
|
||||
// give (inheritable copies of) handles to child process
|
||||
HANDLE me = GetCurrentProcess();
|
||||
DuplicateHandle(me,in_theirs, me,&child_startup_info.hStdInput, 0,TRUE,DUPLICATE_CLOSE_SOURCE|DUPLICATE_SAME_ACCESS);
|
||||
DuplicateHandle(me,out_theirs,me,&child_startup_info.hStdOutput,0,TRUE,DUPLICATE_CLOSE_SOURCE|DUPLICATE_SAME_ACCESS);
|
||||
DuplicateHandle(me,err_theirs,me,&child_startup_info.hStdError, 0,TRUE,DUPLICATE_CLOSE_SOURCE|DUPLICATE_SAME_ACCESS);
|
||||
child_startup_info.dwFlags = STARTF_USESTDHANDLES;
|
||||
}
|
||||
|
||||
// start the child program
|
||||
if (!CreateProcess(app_path,command_line,NULL,NULL,TRUE,0,NULL,NULL,&child_startup_info,&child_process_info)) {
|
||||
ExitProcess(1);
|
||||
}
|
||||
|
||||
// wait for program to terminate
|
||||
DWORD exit_code = 0;
|
||||
if (need_redirection) {
|
||||
delete [] command_line;
|
||||
WaitForSingleObject(child_process_info.hProcess, INFINITE);
|
||||
GetExitCodeProcess(child_process_info.hProcess, &exit_code);
|
||||
}
|
||||
|
||||
// That's all folks!
|
||||
return exit_code;
|
||||
// determine whether we need to wrap console i/o
|
||||
bool need_redirection = false;
|
||||
for (int i = 1 ; i < argc ; ++i) {
|
||||
for (int j = 0 ; j < sizeof(redirect_flags)/sizeof(redirect_flags[0]) ; ++j) {
|
||||
if (strcmp(argv[i],redirect_flags[j]) == 0) {
|
||||
need_redirection = true;
|
||||
goto break_2;
|
||||
}
|
||||
}
|
||||
}
|
||||
break_2:
|
||||
|
||||
// command line
|
||||
TCHAR* command_line = GetCommandLine();
|
||||
if (need_redirection) {
|
||||
// update command line : add --color flag
|
||||
TCHAR* my_command_line = command_line;
|
||||
command_line = new TCHAR[_tcsclen(command_line) + 10];
|
||||
_tcscpy(command_line, my_command_line);
|
||||
_tcscat(command_line, _T(" --color"));
|
||||
}
|
||||
|
||||
// application name
|
||||
TCHAR app_path[2048];
|
||||
GetModuleFileName(NULL/*current process*/, app_path, sizeof(app_path)/sizeof(TCHAR));
|
||||
size_t app_path_length = _tcsclen(app_path);
|
||||
if (app_path_length > 4 && _tcsicmp(app_path + app_path_length - 4, _T(".com")) == 0) {
|
||||
// replace ".com" with ".exe"
|
||||
_tcscpy(app_path + app_path_length - 4, _T(".exe"));
|
||||
} else {
|
||||
// not a .com file, error message
|
||||
fprintf(stderr, "This executable should be named <something>.com\n");
|
||||
}
|
||||
|
||||
// win32 structures for child program
|
||||
STARTUPINFO child_startup_info;
|
||||
memset(&child_startup_info, 0, sizeof(child_startup_info));
|
||||
memset(&child_process_info, 0, sizeof(child_process_info));
|
||||
child_startup_info.cb = sizeof(child_startup_info);
|
||||
|
||||
// setup redirection
|
||||
if (need_redirection) {
|
||||
// Ctrl+C handler
|
||||
SetConsoleCtrlHandler(HandleCtrlEvent, TRUE);
|
||||
|
||||
// create pipes
|
||||
CreatePipe(&in_theirs, &in_mine, NULL, 0);
|
||||
CreatePipe(&out_mine, &out_theirs, NULL, 0);
|
||||
CreatePipe(&err_mine, &err_theirs, NULL, 0);
|
||||
|
||||
// the actual handles
|
||||
in_real = GetStdHandle(STD_INPUT_HANDLE);
|
||||
out_real = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
err_real = GetStdHandle(STD_ERROR_HANDLE);
|
||||
InitEscapeTranslation(out_real);
|
||||
|
||||
// start threads
|
||||
Transfer tranfer_in = {in_real, in_mine, false};
|
||||
Transfer tranfer_out = {out_mine, out_real, true};
|
||||
Transfer tranfer_err = {err_mine, err_real, true};
|
||||
CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)TransferThread,&tranfer_in, 0,NULL);
|
||||
CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)TransferThread,&tranfer_out,0,NULL);
|
||||
CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)TransferThread,&tranfer_err,0,NULL);
|
||||
|
||||
// give (inheritable copies of) handles to child process
|
||||
HANDLE me = GetCurrentProcess();
|
||||
DuplicateHandle(me,in_theirs, me,&child_startup_info.hStdInput, 0,TRUE,DUPLICATE_CLOSE_SOURCE|DUPLICATE_SAME_ACCESS);
|
||||
DuplicateHandle(me,out_theirs,me,&child_startup_info.hStdOutput,0,TRUE,DUPLICATE_CLOSE_SOURCE|DUPLICATE_SAME_ACCESS);
|
||||
DuplicateHandle(me,err_theirs,me,&child_startup_info.hStdError, 0,TRUE,DUPLICATE_CLOSE_SOURCE|DUPLICATE_SAME_ACCESS);
|
||||
child_startup_info.dwFlags = STARTF_USESTDHANDLES;
|
||||
}
|
||||
|
||||
// start the child program
|
||||
if (!CreateProcess(app_path,command_line,NULL,NULL,TRUE,0,NULL,NULL,&child_startup_info,&child_process_info)) {
|
||||
ExitProcess(1);
|
||||
}
|
||||
|
||||
// wait for program to terminate
|
||||
DWORD exit_code = 0;
|
||||
if (need_redirection) {
|
||||
delete [] command_line;
|
||||
WaitForSingleObject(child_process_info.hProcess, INFINITE);
|
||||
GetExitCodeProcess(child_process_info.hProcess, &exit_code);
|
||||
}
|
||||
|
||||
// That's all folks!
|
||||
return exit_code;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Terminating
|
||||
|
||||
/// Handle Ctrl+C
|
||||
BOOL WINAPI HandleCtrlEvent(DWORD type) {
|
||||
DWORD exit_code = 1;
|
||||
// try to exit child process cleanly
|
||||
// TODO: don't exit child on Ctrl+C
|
||||
bool wait = false;
|
||||
if (in_mine != INVALID_HANDLE_VALUE) {
|
||||
CopyFileBuffer(in_mine,":quit\n",6);
|
||||
CopyFileBuffer(out_real,":quit\n",6);
|
||||
wait = true;
|
||||
}
|
||||
if (wait && WaitForSingleObject(child_process_info.hProcess,100) == WAIT_OBJECT_0) {
|
||||
GetExitCodeProcess(child_process_info.hProcess, &exit_code);
|
||||
} else {
|
||||
TerminateProcess(child_process_info.hProcess,1);
|
||||
}
|
||||
// exit this process
|
||||
ExitProcess(exit_code);
|
||||
return TRUE;
|
||||
DWORD exit_code = 1;
|
||||
// try to exit child process cleanly
|
||||
// TODO: don't exit child on Ctrl+C
|
||||
bool wait = false;
|
||||
if (in_mine != INVALID_HANDLE_VALUE) {
|
||||
CopyFileBuffer(in_mine,":quit\n",6);
|
||||
CopyFileBuffer(out_real,":quit\n",6);
|
||||
wait = true;
|
||||
}
|
||||
if (wait && WaitForSingleObject(child_process_info.hProcess,100) == WAIT_OBJECT_0) {
|
||||
GetExitCodeProcess(child_process_info.hProcess, &exit_code);
|
||||
} else {
|
||||
TerminateProcess(child_process_info.hProcess,1);
|
||||
}
|
||||
// exit this process
|
||||
ExitProcess(exit_code);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : I/O redirection
|
||||
|
||||
/// Copy a buffer to an output handle
|
||||
void CopyFileBuffer(HANDLE output, char* buffer, DWORD size) {
|
||||
DWORD pos = 0, bytes_written;
|
||||
while (pos < size) {
|
||||
WriteFile(output, buffer + pos, size - pos, &bytes_written, NULL);
|
||||
pos += bytes_written;
|
||||
}
|
||||
DWORD pos = 0, bytes_written;
|
||||
while (pos < size) {
|
||||
WriteFile(output, buffer + pos, size - pos, &bytes_written, NULL);
|
||||
pos += bytes_written;
|
||||
}
|
||||
}
|
||||
|
||||
/// Copy a buffer to an output handle, handling escape code
|
||||
void CopyFileBufferWithEscape(HANDLE output, char* buffer, DWORD size, bool handle_escapes) {
|
||||
if (!handle_escapes) {
|
||||
CopyFileBuffer(output, buffer, size);
|
||||
return;
|
||||
}
|
||||
DWORD pos = 0;
|
||||
while (pos < size) {
|
||||
// find next escape code, "\x1B["
|
||||
DWORD next_pos = pos;
|
||||
while (next_pos < size &&
|
||||
(buffer[next_pos] != '\x1B' ||
|
||||
(next_pos + 1 >= size || buffer[next_pos+1] != '['))) ++next_pos;
|
||||
// copy part before next escape
|
||||
CopyFileBuffer(output, buffer+pos, next_pos-pos);
|
||||
pos = next_pos;
|
||||
// is this an escape code?
|
||||
if (pos + 1 < size) {
|
||||
// handle escape code
|
||||
int argv[10] = {0}, argc = 1;
|
||||
for (pos += 2 ; pos < size ; ++pos) {
|
||||
if (buffer[pos] == ';') {
|
||||
++argc;
|
||||
} else if (buffer[pos] >= '0' && buffer[pos] <= '9') {
|
||||
argv[argc-1] = 10 * argv[argc-1] + buffer[pos] - '0';
|
||||
} else {
|
||||
PerformEscapeCode(output, buffer[pos], argc, argv);
|
||||
pos++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!handle_escapes) {
|
||||
CopyFileBuffer(output, buffer, size);
|
||||
return;
|
||||
}
|
||||
DWORD pos = 0;
|
||||
while (pos < size) {
|
||||
// find next escape code, "\x1B["
|
||||
DWORD next_pos = pos;
|
||||
while (next_pos < size &&
|
||||
(buffer[next_pos] != '\x1B' ||
|
||||
(next_pos + 1 >= size || buffer[next_pos+1] != '['))) ++next_pos;
|
||||
// copy part before next escape
|
||||
CopyFileBuffer(output, buffer+pos, next_pos-pos);
|
||||
pos = next_pos;
|
||||
// is this an escape code?
|
||||
if (pos + 1 < size) {
|
||||
// handle escape code
|
||||
int argv[10] = {0}, argc = 1;
|
||||
for (pos += 2 ; pos < size ; ++pos) {
|
||||
if (buffer[pos] == ';') {
|
||||
++argc;
|
||||
} else if (buffer[pos] >= '0' && buffer[pos] <= '9') {
|
||||
argv[argc-1] = 10 * argv[argc-1] + buffer[pos] - '0';
|
||||
} else {
|
||||
PerformEscapeCode(output, buffer[pos], argc, argv);
|
||||
pos++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Thread to transfer text from one handle to another
|
||||
DWORD WINAPI TransferThread(Transfer* transfer) {
|
||||
while (true) {
|
||||
// read
|
||||
char buffer[1024];
|
||||
DWORD bytes_read;
|
||||
BOOL ok = ReadFile(transfer->from, buffer, sizeof(buffer), &bytes_read, NULL);
|
||||
if (ok && bytes_read == 0) {
|
||||
// end of file: close handle
|
||||
CloseHandle(transfer->to);
|
||||
transfer->to = INVALID_HANDLE_VALUE;
|
||||
break;
|
||||
}
|
||||
// write
|
||||
CopyFileBufferWithEscape(transfer->to, buffer, bytes_read, transfer->escapes);
|
||||
}
|
||||
return 0;
|
||||
while (true) {
|
||||
// read
|
||||
char buffer[1024];
|
||||
DWORD bytes_read;
|
||||
BOOL ok = ReadFile(transfer->from, buffer, sizeof(buffer), &bytes_read, NULL);
|
||||
if (ok && bytes_read == 0) {
|
||||
// end of file: close handle
|
||||
CloseHandle(transfer->to);
|
||||
transfer->to = INVALID_HANDLE_VALUE;
|
||||
break;
|
||||
}
|
||||
// write
|
||||
CopyFileBufferWithEscape(transfer->to, buffer, bytes_read, transfer->escapes);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Escape codes
|
||||
@@ -246,64 +246,64 @@ WORD original_attributes;
|
||||
|
||||
/// Initialization for escape translation
|
||||
void InitEscapeTranslation(HANDLE console) {
|
||||
CONSOLE_SCREEN_BUFFER_INFO screen_buffer;
|
||||
GetConsoleScreenBufferInfo(console, &screen_buffer);
|
||||
original_attributes = screen_buffer.wAttributes;
|
||||
CONSOLE_SCREEN_BUFFER_INFO screen_buffer;
|
||||
GetConsoleScreenBufferInfo(console, &screen_buffer);
|
||||
original_attributes = screen_buffer.wAttributes;
|
||||
}
|
||||
|
||||
/// Perform an escape code
|
||||
void PerformEscapeCode(HANDLE console, char command, int argc, int argv[]) {
|
||||
switch (command) {
|
||||
case 'm': {
|
||||
CONSOLE_SCREEN_BUFFER_INFO screen_buffer;
|
||||
GetConsoleScreenBufferInfo(console, &screen_buffer);
|
||||
WORD attributes = screen_buffer.wAttributes;
|
||||
for (int i = 0 ; i < argc ; ++i) {
|
||||
int major = argv[i] / 10, minor = argv[i] % 10;
|
||||
if (argv[i] == 0) { // reset
|
||||
attributes = original_attributes;
|
||||
} else if (argv[i] == 1) { // bold
|
||||
attributes |= FOREGROUND_INTENSITY;
|
||||
} else if (argv[i] == 7) { // reverse
|
||||
#if BACKGROUND_RED != FOREGROUND_RED << 4
|
||||
#error Color codes are not what I expected them to be
|
||||
#endif
|
||||
attributes = (attributes & (FOREGROUND_COLOR | FOREGROUND_INTENSITY)) << 4
|
||||
| (attributes & (BACKGROUND_COLOR | BACKGROUND_INTENSITY)) >> 4;
|
||||
} else if (argv[i] == 21 || argv[i] == 22) { // not bold
|
||||
attributes &= ~FOREGROUND_INTENSITY;
|
||||
} else if (major == 3) {
|
||||
// foreground color
|
||||
attributes &= ~FOREGROUND_COLOR;
|
||||
if (minor == 9) { // reset
|
||||
attributes |= original_attributes & FOREGROUND_COLOR;
|
||||
} else {
|
||||
attributes |= (minor & 1 ? FOREGROUND_RED : 0)
|
||||
| (minor & 2 ? FOREGROUND_GREEN : 0)
|
||||
| (minor & 4 ? FOREGROUND_BLUE : 0);
|
||||
}
|
||||
} else if (major == 4) {
|
||||
// background color
|
||||
attributes &= ~BACKGROUND_COLOR;
|
||||
if (minor == 9) { // reset
|
||||
attributes |= original_attributes & BACKGROUND_COLOR;
|
||||
} else {
|
||||
attributes |= (minor & 1 ? BACKGROUND_RED : 0)
|
||||
| (minor & 2 ? BACKGROUND_GREEN : 0)
|
||||
| (minor & 4 ? BACKGROUND_BLUE : 0);
|
||||
}
|
||||
} else {
|
||||
// other, ignore
|
||||
}
|
||||
}
|
||||
if (attributes != screen_buffer.wAttributes) {
|
||||
SetConsoleTextAttribute(console, attributes);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break; // unsupported command, ignore
|
||||
}
|
||||
switch (command) {
|
||||
case 'm': {
|
||||
CONSOLE_SCREEN_BUFFER_INFO screen_buffer;
|
||||
GetConsoleScreenBufferInfo(console, &screen_buffer);
|
||||
WORD attributes = screen_buffer.wAttributes;
|
||||
for (int i = 0 ; i < argc ; ++i) {
|
||||
int major = argv[i] / 10, minor = argv[i] % 10;
|
||||
if (argv[i] == 0) { // reset
|
||||
attributes = original_attributes;
|
||||
} else if (argv[i] == 1) { // bold
|
||||
attributes |= FOREGROUND_INTENSITY;
|
||||
} else if (argv[i] == 7) { // reverse
|
||||
#if BACKGROUND_RED != FOREGROUND_RED << 4
|
||||
#error Color codes are not what I expected them to be
|
||||
#endif
|
||||
attributes = (attributes & (FOREGROUND_COLOR | FOREGROUND_INTENSITY)) << 4
|
||||
| (attributes & (BACKGROUND_COLOR | BACKGROUND_INTENSITY)) >> 4;
|
||||
} else if (argv[i] == 21 || argv[i] == 22) { // not bold
|
||||
attributes &= ~FOREGROUND_INTENSITY;
|
||||
} else if (major == 3) {
|
||||
// foreground color
|
||||
attributes &= ~FOREGROUND_COLOR;
|
||||
if (minor == 9) { // reset
|
||||
attributes |= original_attributes & FOREGROUND_COLOR;
|
||||
} else {
|
||||
attributes |= (minor & 1 ? FOREGROUND_RED : 0)
|
||||
| (minor & 2 ? FOREGROUND_GREEN : 0)
|
||||
| (minor & 4 ? FOREGROUND_BLUE : 0);
|
||||
}
|
||||
} else if (major == 4) {
|
||||
// background color
|
||||
attributes &= ~BACKGROUND_COLOR;
|
||||
if (minor == 9) { // reset
|
||||
attributes |= original_attributes & BACKGROUND_COLOR;
|
||||
} else {
|
||||
attributes |= (minor & 1 ? BACKGROUND_RED : 0)
|
||||
| (minor & 2 ? BACKGROUND_GREEN : 0)
|
||||
| (minor & 4 ? BACKGROUND_BLUE : 0);
|
||||
}
|
||||
} else {
|
||||
// other, ignore
|
||||
}
|
||||
}
|
||||
if (attributes != screen_buffer.wAttributes) {
|
||||
SetConsoleTextAttribute(console, attributes);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break; // unsupported command, ignore
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
|
||||
Reference in New Issue
Block a user