#include #include #include #include #include "client.h" #include "config.h" #include "version.h" #ifdef _WIN32 #include #include #include #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 #endif #endif #if defined(GRAB_CRASH) #ifdef USE_BOOST #include namespace fs = boost::filesystem; #else #include namespace fs = std::filesystem; #endif #include #endif std::shared_ptr g_Config = nullptr; int parse_cmd(int argc, char** argv, CmdParam& param) { std::string intro(fmt::format("tsc cmd introduce, version: {}\nopensource: {}", VERSION_NUM, VERSION_URL)); CLI::App app(intro); app.add_option("-u, --use", param.use_config, "使用服务器地址组(值为使用--show中显示的序号)"); app.add_option("-a, --append", param.appendValue, "添加服务器地址组(地址格式:127.0.0.1:9898:注释)"); app.add_flag("-s, --show", param.showValue, "查看服务器地址组"); app.add_option("-r, --remove", param.removeValue, "移除服务器地址组(值为使用--show中显示的序号)"); app.add_flag("-d, --direct", param.direct_use, "添加服务器时直接使用此服务器。"); app.add_flag("-l, --last", param.last_use, "直接使用之前最后一次使用的服务器。"); app.add_flag("-n, --null", param.null_use, "先运行在选择服务器。"); app.add_option("-c, --connect", param.connectValue, "直连服务器((地址格式:127.0.0.1:9898)。"); if (argc == 1) { std::cout << app.help() << std::endl; return 0; } // 这里的 CLI11_PARSE 在程序没有输入或者仅输入--help(-h)时,会直接返回,后面代码都不会执行。 // 当有自定义的参数被输入时,后面代码会执行。 try { CLI11_PARSE(app, argc, argv); } catch (const CLI::ParseError& e) { return app.exit(e); } param.parsed = true; return 0; } bool select_server(const std::vector& sets, std::string& ip, long& port) { TLOGI("Please Select a Server:"); if (sets.empty()) { TLOGE("No servers available to select."); return false; } // 打印所有服务器选项(带编号) for (size_t i = 0; i < sets.size(); ++i) { const auto& server = sets[i]; if (server.comment.empty()) { TLOGI("[{}] {}:{}", i + 1, server.ip, server.port); } else { TLOGI("[{}] {}:{} ({})", i + 1, server.ip, server.port, server.comment); } } while (true) { TLOGW("Enter server number (or 'exit' to cancel): "); std::string input; std::getline(std::cin, input); // 检查是否输入了退出命令 if (input == "exit" || input == "quit" || input == "q") { TLOGD("User canceled server selection."); return false; } // 检查输入是否为空或非数字 if (input.empty() || !std::all_of(input.begin(), input.end(), ::isdigit)) { TLOGE("Invalid input '{}'. Please enter a valid number or 'exit'.", input); continue; } // 转换为数字 int choice = std::stoi(input); // 检查数字是否在有效范围内 if (choice < 1 || choice > static_cast(sets.size())) { TLOGE("Invalid choice '{}'. Please select a number between 1 and {}.", choice, sets.size()); continue; } // 选择成功,返回对应的 IP 和端口 const auto& selected = sets[choice - 1]; ip = selected.ip; port = selected.port; TLOGI("Selected server: {}:{} ({})", ip, port, selected.comment); return true; } } bool exec_cmd(CmdParam& param, bool& run) { run = false; // 如果是展示 if (param.showValue) { std::vector set; if (!g_Config->read_ini(set)) { return false; } for (const auto& item : set) { TLOGI("{} => {}:{} {}", item.group, item.ip, item.port, item.comment); } return true; } if (param.use_config != -1) { run = true; return true; } if (!param.appendValue.empty() && !param.removeValue.empty()) { TLOGW("append and remove can't simultaneous operate!"); return false; } if (!param.appendValue.empty()) { std::regex pattern(R"((\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}):(\d+)(?::(.*))?)"); std::smatch matches; std::string ip, port, comment; if (std::regex_match(param.appendValue, matches, pattern)) { ip = matches[1].str(); port = matches[2].str(); comment = matches.size() > 3 ? matches[3].str() : "default"; } auto id = g_Config->append_ini(ip, std::stol(port), comment); if (id < 0) { TLOGW("add {}:{} failed.", ip, port); return false; } TLOGI("add {}:{} success.", ip, port); param.use_config = id; return true; } if (!param.removeValue.empty()) { if (!g_Config->remove_ini(std::stol(param.removeValue))) { TLOGW("remove config num=[{}] failed, please check!", param.removeValue); return false; } TLOGI("remove config num=[{}] success!", param.removeValue); return true; } if (param.last_use || param.null_use) { return true; } TLOGW("not matched!", param.removeValue); return false; } int main(int argc, char* argv[]) { #if defined(GRAB_CRASH) auto config_dir = COfPath::get_config_dir("transm", true); auto err = fs::path(config_dir).append("errs").string(); backward::SetDumpFileSavePath(err); backward::SetDumpLogSavePath(err); CRASHELPER_MARK_ENTRY(); #endif #ifdef _WIN32 HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); DWORD mode; GetConsoleMode(hConsole, &mode); mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; SetConsoleMode(hConsole, mode); #endif std::shared_ptr deleter(new int(), [](int* p) { fc_recovery_color(); delete p; }); g_Config = std::make_shared(); if (!g_Config->baseInit()) { return -1; } bool run = false; CmdParam param; parse_cmd(argc, argv, param); if (!param.parsed) { return 0; } std::string ip; long port{}; if (!param.connectValue.empty()) { std::regex pattern(R"((\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}):(\d+))"); std::smatch matches; if (std::regex_match(param.connectValue, matches, pattern) && matches.size() == 3) { ip = matches[1].str(); port = std::stol(matches[2].str()); run = true; } else { TLOGE("Invalid connect value [{}]", param.connectValue); return -1; } } else if (!exec_cmd(param, run)) { TLOGW("exec_cmd failed!"); return -1; } if (!run && !param.direct_use && !param.last_use && !param.null_use) { return 0; } if (ip.empty()) { std::vector sets; if (!g_Config->read_ini(sets)) { return -1; } if (param.null_use) { if (!select_server(sets, ip, port)) { return -1; } } else { if (param.last_use) { if (!g_Config->get_last_use(ip, port)) { return -1; } } else { TransmSet use; if (!g_Config->get_ini(sets, param.use_config, use)) { TLOGW("Not found config by num:[{}]", param.use_config); return -1; } ip = use.ip; port = use.port; } } g_Config->save_last_use(ip, port); } TLOGI("Build At {} {} under {} on {}", __DATE__, __TIME__, VERSION_GIT_COMMIT, VERSION_GIT_BRANCH); TLOGI("use ip => [{}], port => [{}]", ip, port); std::shared_ptr client = std::make_shared(); client->run(ip, std::to_string(port), g_Config->get_config_dir()); TLOGI("exit =========="); return 0; }