#include "scp_args.h" #include namespace roro::term { namespace { std::vector words(const std::string& text) { std::vector out; for (size_t at = 0; at < text.size();) { size_t end = text.find(' ', at); if (end == std::string::npos) end = text.size(); if (end > at) out.push_back(text.substr(at, end - at)); at = end + 1; } return out; } // user@host:path, as opposed to a path on the card. bool isRemote(const std::string& w) { if (w.empty() || w[0] == '/') return false; size_t at = w.find('@'), colon = w.find(':'); return at != std::string::npos && colon != std::string::npos && at < colon; } std::string baseName(const std::string& path) { size_t slash = path.rfind('/'); return slash == std::string::npos ? path : path.substr(slash + 1); } } // namespace std::string parseScp(const std::string& args, ScpArgs& out) { static const char* kUsage = "scp [-f] [-P port] user@host:path | scp [-f] [-P port] user@host:path "; std::vector w = words(args); long port = 0; bool replace = false; while (!w.empty() && w[0][0] == '-') { if (w[0] == "-f") { replace = true; w.erase(w.begin()); } else if (w[0] == "-P" && w.size() >= 2) { port = 0; for (char c : w[1]) { if (c < '0' || c > '9' || port > 65535) return "a port from 1 to 65535"; port = port * 10 + (c - '0'); } if (port < 1 || port > 65535) return "a port from 1 to 65535"; w.erase(w.begin(), w.begin() + 2); } else { return kUsage; } } if (w.size() != 2) return kUsage; bool firstRemote = isRemote(w[0]), secondRemote = isRemote(w[1]); if (firstRemote == secondRemote) return firstRemote ? "one side has to be on the card, not both on servers" : kUsage; ScpArgs a; a.upload = secondRemote; a.replace = replace; const std::string& remote = a.upload ? w[1] : w[0]; std::string local = a.upload ? w[0] : w[1]; size_t colon = remote.find(':'); std::string why = parseSshTarget(remote.substr(0, colon), a.target); if (!why.empty()) return why; if (port) a.target.port = static_cast(port); a.remote = remote.substr(colon + 1); if (a.remote.empty()) return "say where on " + a.target.host + ": user@host:path"; if (local.empty() || local[0] != '/') return "a path on the card starts with a slash"; if (local.back() == '/') { if (a.upload) return "that's a folder: scp copies one file"; std::string name = baseName(a.remote); if (name.empty()) return "name the file to fetch"; local += name; } a.local = local; out = a; return ""; } } // namespace roro::term