diff --git a/src/cli/cli_main.cpp b/src/cli/cli_main.cpp index 20cdbbc0..552dceb3 100644 --- a/src/cli/cli_main.cpp +++ b/src/cli/cli_main.cpp @@ -26,6 +26,7 @@ CLISetInterface::CLISetInterface(const SetP& set, bool quiet) throw Error(_("Can not run command line interface without a console;\nstart MSE with \"mse.com --cli\"")); } ei.directory_relative = ei.directory_absolute = wxGetCwd(); + ei.allow_writes_outside = true; setSet(set); run(); } @@ -100,6 +101,8 @@ void CLISetInterface::showUsage() { cli << _(" :load 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 working directory.\n"); cli << _(" :! Perform a shell command.\n"); cli << _("\n Commands can be abreviated to their first letter if there is no ambiguity.\n\n"); } @@ -122,17 +125,30 @@ void CLISetInterface::handleCommand(const String& command) { showUsage(); } else if (before == _(":l") || before == _(":load")) { if (arg.empty()) { - cli << _("Give a filename to open.\n"); + cli.showError(_("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 == _(":c") || before == _(":cd")) { + if (arg.empty()) { + cli.showError(_("Give a new working directory.")); + } else { + if (!wxSetWorkingDirectory(arg)) { + cli.showError(_("Can't change working directory to ")+arg); + } else { + ei.directory_relative = ei.directory_absolute = wxGetCwd(); + } + } + } else if (before == _(":pwd") || before == _(":p")) { + cli << ei.directory_absolute << ENDL; } else if (before == _(":!")) { if (arg.empty()) { - cli << _("Give a shell command to execute.\n"); + cli.showError(_("Give a shell command to execute.")); } else { #ifdef __WXMSW__ _wsystem(arg.c_str()); @@ -144,7 +160,7 @@ void CLISetInterface::handleCommand(const String& command) { #endif } } else { - cli << _("Unknown command, type :help for help.\n"); + cli.showError(_("Unknown command, type :help for help.")); } } else if (command == _("exit") || command == _("quit")) { cli << _("Use :quit to quit\n"); diff --git a/src/data/export_template.cpp b/src/data/export_template.cpp index d3038d37..8a0759e5 100644 --- a/src/data/export_template.cpp +++ b/src/data/export_template.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include // ----------------------------------------------------------------------------- : Export template, basics @@ -45,3 +46,5 @@ IMPLEMENT_REFLECTION(ExportTemplate) { // ----------------------------------------------------------------------------- : ExportInfo IMPLEMENT_DYNAMIC_ARG(ExportInfo*, export_info, nullptr); + +ExportInfo::ExportInfo() : allow_writes_outside(false) {} diff --git a/src/data/export_template.hpp b/src/data/export_template.hpp index 9bde4da2..a0118864 100644 --- a/src/data/export_template.hpp +++ b/src/data/export_template.hpp @@ -45,12 +45,15 @@ class ExportTemplate : public Packaged { /// Information that can be used by export functions struct ExportInfo { + ExportInfo(); + SetP set; ///< The set that is being exported ExportTemplateP export_template; ///< The export template used String directory_relative; ///< The directory for storing extra files (or "" if !export->create_directory) /// This is just the directory name String directory_absolute; ///< The absolute path of the directory map exported_images; ///< Images (from symbol font) already exported, and their size + bool allow_writes_outside; ///< Can files outside the directory be written to? }; DECLARE_DYNAMIC_ARG(ExportInfo*, export_info); diff --git a/src/script/functions/export.cpp b/src/script/functions/export.cpp index 8f0cd42e..f0b42f32 100644 --- a/src/script/functions/export.cpp +++ b/src/script/functions/export.cpp @@ -34,6 +34,29 @@ void guard_export_info(const String& fun, bool need_template = false) { } } +/// Find an absolute filename for a relative filename from an export template, +/// Returns the absolute filename, and may modify the relative name. +String get_export_full_path(String& rel_name) { + ExportInfo& ei = *export_info(); + // the absolute path + wxFileName fn(rel_name); + fn.Normalize(wxPATH_NORM_ALL, ei.directory_absolute); + if (!ei.allow_writes_outside) { + // check if path is okay + wxFileName fn2(_("x")); + fn2.SetPath(ei.directory_absolute); + fn2.Normalize(wxPATH_NORM_ALL, ei.directory_absolute); + String p1 = fn.GetFullPath(); + String p2 = fn2.GetFullPath(); + p2.resize(p2.size() - 1); // drop the x + if (p2.empty() || p1.size() < p2.size() || p1.substr(0,p2.size()-1) != p2.substr(0,p2.size()-1)) { + throw ScriptError(_("Not a relative filename: ") + rel_name); + } + } + rel_name = fn.GetFullName(); + return fn.GetFullPath(); +} + // ----------------------------------------------------------------------------- : HTML // An HTML tag @@ -351,15 +374,16 @@ SCRIPT_FUNCTION(to_text) { SCRIPT_FUNCTION(copy_file) { guard_export_info(_("copy_file")); SCRIPT_PARAM_C(String, input); // file to copy - ExportInfo& ei = *export_info(); - wxFileName fn(input); - fn.SetPath(ei.directory_absolute); + // output path + String out_name = input; + String out_path = get_export_full_path(out_name); // copy + ExportInfo& ei = *export_info(); InputStreamP in = ei.export_template->openIn(input); - wxFileOutputStream out(fn.GetFullPath()); - if (!out.Ok()) throw Error(_("Unable to open file '") + fn.GetFullPath() + _("' for output")); + wxFileOutputStream out(out_path); + if (!out.Ok()) throw Error(_("Unable to open file '") + out_path + _("' for output")); out.Write(*in); - SCRIPT_RETURN(fn.GetFullName()); + SCRIPT_RETURN(out_name); } // write a file to the destination directory @@ -367,29 +391,26 @@ SCRIPT_FUNCTION(write_text_file) { guard_export_info(_("write_text_file")); SCRIPT_PARAM_C(String, input); // text to write SCRIPT_PARAM(String, file); // file to write to - // filename - wxFileName fn; - fn.SetPath(export_info()->directory_absolute); - fn.SetFullName(file); + // output path + String out_path = get_export_full_path(file); // write - wxFileOutputStream out(fn.GetFullPath()); - if (!out.Ok()) throw Error(_("Unable to open file '") + fn.GetFullPath() + _("' for output")); + wxFileOutputStream out(out_path); + if (!out.Ok()) throw Error(_("Unable to open file '") + out_path + _("' for output")); wxTextOutputStream tout(out); tout.WriteString(BYTE_ORDER_MARK); tout.WriteString(input); - SCRIPT_RETURN(fn.GetFullName()); + SCRIPT_RETURN(file); } SCRIPT_FUNCTION(write_image_file) { guard_export_info(_("write_image_file")); - ExportInfo& ei = *export_info(); - // filename + // output path SCRIPT_PARAM(String, file); // file to write to - wxFileName fn; - fn.SetPath(ei.directory_absolute); - fn.SetFullName(file); - if (ei.exported_images.find(fn.GetFullName()) != ei.exported_images.end()) { - SCRIPT_RETURN(fn.GetFullName()); // already written an image with this name + String out_path = get_export_full_path(file); + // duplicates? + ExportInfo& ei = *export_info(); + if (ei.exported_images.find(file) != ei.exported_images.end()) { + SCRIPT_RETURN(file); // already written an image with this name } // get image SCRIPT_PARAM_C(ScriptValueP, input); @@ -405,9 +426,9 @@ SCRIPT_FUNCTION(write_image_file) { } if (!image.Ok()) throw Error(_("Unable to generate image for file ") + file); // write - image.SaveFile(fn.GetFullPath()); - ei.exported_images.insert(make_pair(fn.GetFullName(), wxSize(image.GetWidth(), image.GetHeight()))); - SCRIPT_RETURN(fn.GetFullName()); + image.SaveFile(out_path); + ei.exported_images.insert(make_pair(file, wxSize(image.GetWidth(), image.GetHeight()))); + SCRIPT_RETURN(file); } // ----------------------------------------------------------------------------- : Init