// This file is part of ClassicAPI. // // ClassicAPI is free software: you can redistribute it and/or modify it under the terms // of the GNU General Public License as published by the Free Software Foundation, either // version 3 of the License, or (at your option) any later version. // // ClassicAPI is distributed in the hope that it will be useful, but WITHOUT ANY // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along with // ClassicAPI. If not, see . #include "CommandLine.h" #include namespace Config::CommandLine { namespace { bool IsSpace(char c) { return c == ' ' || c == '\t'; } char Lower(char c) { return (c >= 'A' && c <= 'Z') ? static_cast(c - 'A' + 'a') : c; } // Stops at `a`'s terminator rather than reading `len` bytes unconditionally, so // a switch name at the very end of the command line cannot read past it. bool EqualsIgnoreCase(const char *a, const char *b, size_t len) { for (size_t i = 0; i < len; ++i) { if (a[i] == '\0' || Lower(a[i]) != Lower(b[i])) return false; } return true; } size_t Length(const char *s) { size_t n = 0; while (s[n] != '\0') ++n; return n; } // Start of the value token for `-name`, or null when the switch is absent. const char *FindValue(const char *name) { const char *cmdLine = ::GetCommandLineA(); if (cmdLine == nullptr || name == nullptr) return nullptr; const size_t nameLen = Length(name); if (nameLen == 0) return nullptr; for (const char *p = cmdLine; *p != '\0'; ++p) { if (*p != '-' && *p != '/') continue; // Only at a token boundary, so a path containing the name cannot match. if (p != cmdLine && !IsSpace(p[-1]) && p[-1] != '"') continue; if (!EqualsIgnoreCase(p + 1, name, nameLen)) continue; const char *after = p + 1 + nameLen; if (*after == '=') { ++after; } else if (IsSpace(*after)) { while (IsSpace(*after)) ++after; } else { continue; // a longer switch that merely starts with `name` } return after; } return nullptr; } // Walks the token at `p`, honoring double quotes so a value containing spaces // arrives whole. `emit` takes each character and returns whether it was kept; // the walk stops as a failure the moment one is not. template bool ReadToken(const char *p, Emit emit) { const bool quoted = (*p == '"'); if (quoted) ++p; size_t n = 0; while (*p != '\0' && (quoted ? (*p != '"') : !IsSpace(*p))) { if (!emit(*p++)) return false; ++n; } return n > 0; } } // namespace bool Value(const char *name, char *out, size_t outSize) { if (out == nullptr || outSize == 0) return false; out[0] = '\0'; const char *p = FindValue(name); if (p == nullptr) return false; size_t n = 0; const bool ok = ReadToken(p, [&](char c) { if (n + 1 >= outSize) return false; out[n++] = c; return true; }); out[n] = '\0'; return ok; } bool Value(const char *name, std::string &out) { out.clear(); const char *p = FindValue(name); if (p == nullptr) return false; return ReadToken(p, [&](char c) { out.push_back(c); return true; }); } } // namespace Config::CommandLine