diff --git a/builtins.cpp b/builtins.cpp index dce601f..2da1968 100644 --- a/builtins.cpp +++ b/builtins.cpp @@ -17,9 +17,11 @@ #include #include +#include #include "cxx/string_splitter.h" #include "cxx/filesystem.h" +#include "cxx/mapped_file.h" //MAXPATHLEN #include @@ -836,6 +838,7 @@ int builtin_which(Environment &env, const std::vector &tokens, cons "execute", "exists", "export", + "help", "false", // not in MPW "parameters", "quote", @@ -1055,6 +1058,168 @@ int builtin_quit(Environment &, const std::vector &tokens, const fd } +namespace { + template + Iter find_entry(Iter begin, Iter end) { + for(;;) { + begin = std::find(begin, end, '\n'); + if (begin == end) return end; + if (std::distance(begin, end) < 3) return end; + ++begin; + if (begin[0] == '-' && begin[1] == '\n') return begin; + } + } + + template + bool help_name_match(Iter1 begin1, Iter1 end1, Iter2 begin2, Iter2 end2) { + + if (std::distance(begin2, end2) <= std::distance(begin1, end1)) return false; + + for( ; begin1 != end1; ++begin1, ++begin2) { + if (tolower(*begin1) != tolower(*begin2)) return false; + } + + if (isspace(*begin2)) return true; + return false; + } + +} +bool help_helper(const mapped_file &f, const fdmask &fds, const std::string &cmd) { + + /* + * format is: + * -\n + * name whitespace + * .... + * -\n + */ + + if (f.size() < 2) return false; + auto iter = f.begin(); + auto end = f.end(); + + if (iter[0] != '-' && iter[1] != '\n') { + iter = find_entry(iter, end); + } + + if (cmd.empty()) { + // print first entry + write(stdout, f.begin(), std::distance(f.begin(), iter)); + fdputs("\n", stdout); + return true; + } + + for(;;) { + + if (iter == end) return false; + + iter += 2; + auto next = find_entry(iter, end); + + auto l = std::distance(iter, end); + if (help_name_match(cmd.begin(), cmd.end(), iter, next)) { + + write(stdout, iter, std::distance(iter, next)); + fdputs("\n", stdout); + + return true; + } + + iter = next; + } +} + + + +int builtin_help(Environment &env, const std::vector &tokens, const fdmask &fds) { + + bool error = false; + filesystem::path _f; + + // todo -- -f to specify help file. + auto argv = getopt(tokens, [&](char c){ + switch(tolower(c)) + { + + default: + fdprintf(stderr, "### Help - \"-%c\" is not an option.\n", c); + error = true; + break; + } + }); + + if (error) { + fdputs("# Usage - Help [-f helpfile] command...\n", stderr); + return 1; + } + + + const filesystem::path sd(ToolBox::MacToUnix(env.get("shelldirectory"))); + const filesystem::path hd = sd / "Help"; + + filesystem::path mono; + mapped_file mono_file; + std::error_code ec; + + if (_f.empty()) { + mono = sd / "MPW.Help"; + mono_file = mapped_file(mono, mapped_file::priv, ec); + } else { + + mono = _f; + mono_file = mapped_file(mono, mapped_file::priv, ec); + if (!mono_file && !_f.is_absolute()) { + mono = sd / _f; + mono_file = mapped_file(mono, mapped_file::priv, ec); + } + + if (!mono_file) { + fdprintf(stderr, "### Help: Unable to open %s\n", _f.c_str()); + fdprintf(stderr, "# %s\n", ec.message().c_str()); + return 3; + } + + } + + if (mono_file) { + std::replace(mono_file.begin(), mono_file.end(), '\r', '\n'); + } + + if (argv.empty()) { + help_helper(mono_file, fds, ""); + return 0; + } + + int rv = 0; + for (const auto &cmd : argv) { + + + // 1. check for $MPW:Help:command + filesystem::path p(hd); + p /= cmd; + + mapped_file f(p, mapped_file::priv, ec); + if (!ec) { + std::replace(f.begin(), f.end(), '\r', '\n'); + write(stdout, f.data(), f.size()); + fdputs("\n", stdout); + continue; + } + + + if (mono_file) { + bool ok = help_helper(mono_file, fds, cmd); + if (ok) break; + } + + fdprintf(stderr, "### Help - \"%s\" was not found.\n", cmd.c_str()); + rv = 2; + } + + return rv; +} + + int builtin_execute(Environment &e, const std::vector &tokens, const fdmask &fds) { // runs argv[1] in the current environment. unlike MPW, argv[1] must be a script. diff --git a/builtins.h b/builtins.h index add487f..8181c76 100644 --- a/builtins.h +++ b/builtins.h @@ -14,6 +14,7 @@ int builtin_directory(Environment &e, const std::vector &, const fd int builtin_echo(Environment &e, const std::vector &, const fdmask &); int builtin_exists(Environment &e, const std::vector &, const fdmask &); int builtin_export(Environment &e, const std::vector &, const fdmask &); +int builtin_help(Environment &e, const std::vector &, const fdmask &); int builtin_parameters(Environment &e, const std::vector &, const fdmask &); int builtin_quote(Environment &e, const std::vector &tokens, const fdmask &); int builtin_set(Environment &e, const std::vector &, const fdmask &); diff --git a/command.cpp b/command.cpp index a7609ec..0bea643 100644 --- a/command.cpp +++ b/command.cpp @@ -212,6 +212,7 @@ namespace { {"execute", builtin_execute}, {"exists", builtin_exists}, {"export", builtin_export}, + {"help", builtin_help}, {"parameters", builtin_parameters}, {"quit", builtin_quit}, {"quote", builtin_quote},