refactor: uri parser now does parsing
This commit is contained in:
parent
6b9ce1dda4
commit
c3e5117fcb
@ -52,7 +52,7 @@ std::unique_ptr<ADirective> DirectiveFactory::create(std::string_view type, cons
|
||||
{
|
||||
throw std::invalid_argument("No factory found for directive type: " + std::string(type));
|
||||
}
|
||||
return it->second(name, utils::trimSemi(utils::trim(arg)));
|
||||
return it->second(name, utils::trim(arg, " \t\n\r;"));
|
||||
}
|
||||
|
||||
const std::unordered_map<std::string_view, DirectiveFactory::CreatorFunc> &DirectiveFactory::getFactories()
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
#include <webserv/config/LocationConfig.hpp> // for LocationConfig
|
||||
#include "webserv/config/AConfig.hpp"
|
||||
|
||||
#include <webserv/handler/ErrorHandler.hpp> // for ErrorHandler
|
||||
#include <webserv/handler/FileHandler.hpp>
|
||||
#include <webserv/handler/MIMETypes.hpp> // for MIMETypes
|
||||
@ -8,16 +9,12 @@
|
||||
#include <webserv/log/Log.hpp> // for Log, LOCATION
|
||||
#include <webserv/utils/FileUtils.hpp> // for joinPath, getExtension, isFile, readBinaryFile
|
||||
|
||||
#include <functional> // for identity
|
||||
#include <memory> // for unique_ptr, allocator, make_unique
|
||||
#include <optional> // for optional
|
||||
#include <ranges> // for __find_if_fn, find_if
|
||||
#include <ranges>
|
||||
#include <string> // for basic_string, string, operator+, char_traits
|
||||
#include <utility> // for move
|
||||
#include <vector> // for vector
|
||||
|
||||
FileHandler::FileHandler(const LocationConfig *location, const URIParser &uriParser)
|
||||
: location_(location), uriParser_(uriParser)
|
||||
FileHandler::FileHandler(const AConfig *config, const URIParser &uriParser) : config_(config), uriParser_(uriParser)
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
}
|
||||
@ -35,7 +32,7 @@ std::unique_ptr<HttpResponse> FileHandler::handleFile(const std::string &filepat
|
||||
Log::debug("Serving file: " + filepath + " with MIME type: " + mimeType);
|
||||
if (fileData.empty())
|
||||
{
|
||||
return ErrorHandler::getErrorResponse(Http::StatusCode::NOT_FOUND, location_);
|
||||
return ErrorHandler::getErrorResponse(Http::StatusCode::NOT_FOUND, config_);
|
||||
}
|
||||
// TODO: annoying: For reading files, vector<char> is preferred, but for http data vector<uint8_t> is preferred
|
||||
response->setBody(std::vector<uint8_t>{fileData.begin(), fileData.end()});
|
||||
@ -49,28 +46,28 @@ std::unique_ptr<HttpResponse> FileHandler::handleDirectory(const std::string &di
|
||||
|
||||
if (type == DIRECTORY_INDEX)
|
||||
{
|
||||
auto possible_indexes = location_->get<std::vector<std::string>>("index").value();
|
||||
auto possible_indexes = config_->get<std::vector<std::string>>("index").value();
|
||||
auto first_matching = std::ranges::find_if(possible_indexes, [&](const std::string &index) {
|
||||
return FileUtils::isFile(FileUtils::joinPath(dirpath, index));
|
||||
});
|
||||
if (first_matching == possible_indexes.end())
|
||||
{
|
||||
return ErrorHandler::getErrorResponse(Http::StatusCode::FORBIDDEN, location_);
|
||||
return ErrorHandler::getErrorResponse(Http::StatusCode::FORBIDDEN, config_);
|
||||
}
|
||||
return handleFile(FileUtils::joinPath(dirpath, *first_matching));
|
||||
}
|
||||
if (type == DIRECTORY_AUTOINDEX)
|
||||
{
|
||||
Log::debug("Requested path is a directory: " + dirpath);
|
||||
return ErrorHandler::getErrorResponse(Http::StatusCode::FORBIDDEN, location_);
|
||||
return ErrorHandler::getErrorResponse(Http::StatusCode::FORBIDDEN, config_);
|
||||
}
|
||||
return ErrorHandler::getErrorResponse(Http::StatusCode::NOT_FOUND, location_);
|
||||
return ErrorHandler::getErrorResponse(Http::StatusCode::NOT_FOUND, config_);
|
||||
}
|
||||
|
||||
std::unique_ptr<HttpResponse> FileHandler::getResponse() const
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
std::string filepath = uriParser_.getFilePath();
|
||||
std::string filepath = uriParser_.getFullPath();
|
||||
ResourceType resourceType = getResourceType(filepath);
|
||||
|
||||
switch (resourceType)
|
||||
@ -78,9 +75,9 @@ std::unique_ptr<HttpResponse> FileHandler::getResponse() const
|
||||
case FILE: return handleFile(filepath);
|
||||
case DIRECTORY_AUTOINDEX:
|
||||
case DIRECTORY_INDEX: return handleDirectory(filepath, resourceType);
|
||||
case NOT_FOUND: return ErrorHandler::getErrorResponse(Http::StatusCode::NOT_FOUND, location_);
|
||||
case NOT_FOUND: return ErrorHandler::getErrorResponse(Http::StatusCode::NOT_FOUND, config_);
|
||||
}
|
||||
return ErrorHandler::getErrorResponse(Http::StatusCode::NOT_FOUND, location_);
|
||||
return ErrorHandler::getErrorResponse(Http::StatusCode::NOT_FOUND, config_);
|
||||
}
|
||||
|
||||
FileHandler::ResourceType FileHandler::getResourceType(const std::string &path) const
|
||||
@ -94,11 +91,11 @@ FileHandler::ResourceType FileHandler::getResourceType(const std::string &path)
|
||||
}
|
||||
if (uriParser_.isDirectory())
|
||||
{
|
||||
if (location_->get<std::vector<std::string>>("index").has_value())
|
||||
if (config_->get<std::vector<std::string>>("index").has_value())
|
||||
{
|
||||
return DIRECTORY_INDEX;
|
||||
}
|
||||
if (location_->get<bool>("autoindex").value_or(false))
|
||||
if (config_->get<bool>("autoindex").value_or(false))
|
||||
{
|
||||
return DIRECTORY_AUTOINDEX;
|
||||
}
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "webserv/config/AConfig.hpp"
|
||||
|
||||
#include <webserv/config/LocationConfig.hpp>
|
||||
#include <webserv/handler/URIParser.hpp>
|
||||
#include <webserv/http/HttpResponse.hpp> // for HttpResponse
|
||||
@ -8,18 +10,18 @@
|
||||
#include <memory> // for unique_ptr
|
||||
#include <string> // for string
|
||||
|
||||
class LocationConfig;
|
||||
class AConfig;
|
||||
class URIParser;
|
||||
|
||||
class FileHandler
|
||||
{
|
||||
public:
|
||||
FileHandler(const LocationConfig *location, const URIParser &uriParser);
|
||||
FileHandler(const AConfig *config, const URIParser &uriParser);
|
||||
|
||||
[[nodiscard]] std::unique_ptr<HttpResponse> getResponse() const;
|
||||
|
||||
private:
|
||||
const LocationConfig *location_;
|
||||
const AConfig *config_;
|
||||
const URIParser &uriParser_;
|
||||
|
||||
enum ResourceType : uint8_t
|
||||
|
||||
@ -1,96 +1,155 @@
|
||||
#include <webserv/handler/URIParser.hpp>
|
||||
#include "webserv/config/AConfig.hpp"
|
||||
#include "webserv/utils/FileUtils.hpp"
|
||||
#include "webserv/utils/utils.hpp"
|
||||
|
||||
#include <webserv/config/LocationConfig.hpp> // for LocationConfig
|
||||
#include <webserv/config/ServerConfig.hpp> // for ServerConfig
|
||||
#include <webserv/handler/URIParser.hpp>
|
||||
|
||||
#include <optional> // for optional
|
||||
|
||||
#include <stddef.h> // for size_t
|
||||
#include <sys/stat.h> // for stat, S_ISDIR, S_ISREG
|
||||
|
||||
URIParser::URIParser(const std::string &uri, const ServerConfig &serverConfig) : _locationConfig(nullptr)
|
||||
URIParser::URIParser(const std::string &uri, const ServerConfig &serverConfig)
|
||||
: uriTrimmed_(utils::trim(uri, "/")), config_(matchConfig(uriTrimmed_, serverConfig))
|
||||
{
|
||||
parseUri(uri);
|
||||
parseFullpath();
|
||||
}
|
||||
|
||||
const AConfig *URIParser::matchConfig(const std::string &uri, const ServerConfig &serverConfig)
|
||||
{
|
||||
const auto &locations = serverConfig.getLocationPaths();
|
||||
size_t maxMatchLength = 0;
|
||||
const AConfig *bestMatch = &serverConfig;
|
||||
|
||||
size_t maxMatchLength = 0;
|
||||
for (const auto &locationPath : locations)
|
||||
{
|
||||
if (uri.starts_with((locationPath == "/") ? locationPath : locationPath + "/"))
|
||||
{ // TODO HMHMMz why does it need to end on a /?
|
||||
if (uri.empty() && locationPath == "/")
|
||||
{
|
||||
return serverConfig.getLocation(locationPath);
|
||||
}
|
||||
if (uri.starts_with(utils::trim(locationPath, "/")))
|
||||
{
|
||||
if (locationPath.length() > maxMatchLength)
|
||||
{
|
||||
maxMatchLength = locationPath.length();
|
||||
_locationConfig = serverConfig.getLocation(locationPath);
|
||||
bestMatch = serverConfig.getLocation(locationPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
return bestMatch;
|
||||
}
|
||||
|
||||
void URIParser::parseUri(const std::string &uri)
|
||||
{
|
||||
if (config_->getType() == "server")
|
||||
{
|
||||
fullPath_ = FileUtils::joinPath(config_->get<std::string>("root").value_or(""), uriTrimmed_);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto const *locConfig = dynamic_cast<LocationConfig const *>(config_);
|
||||
std::string locTrimmed = utils::trim(locConfig->getPath(), "/");
|
||||
std::string uriSub = uri.substr(locTrimmed.length());
|
||||
fullPath_ = FileUtils::joinPath(locConfig->get<std::string>("root").value_or(""), uriSub);
|
||||
}
|
||||
|
||||
size_t fragmentPos = fullPath_.find_first_of('#');
|
||||
if (fragmentPos != std::string::npos)
|
||||
{
|
||||
fragment_ = fullPath_.substr(fragmentPos + 1);
|
||||
fullPath_ = fullPath_.substr(0, fragmentPos);
|
||||
}
|
||||
|
||||
size_t queryPos = fullPath_.find_first_of('?');
|
||||
if (queryPos != std::string::npos)
|
||||
{
|
||||
query_ = fullPath_.substr(queryPos + 1);
|
||||
fullPath_ = fullPath_.substr(0, queryPos);
|
||||
}
|
||||
}
|
||||
|
||||
root_ = _locationConfig != nullptr ? _locationConfig->get<std::string>("root").value_or("") : "";
|
||||
if (!root_.empty() && root_.back() == '/')
|
||||
void URIParser::parseFullpath()
|
||||
{
|
||||
root_.pop_back(); // Remove trailing slash to avoid double slashes in path
|
||||
auto uriSegments = utils::split(fullPath_, '/');
|
||||
|
||||
for (const auto &segment : uriSegments)
|
||||
{
|
||||
std::string curDir = FileUtils::joinPath(dir_, segment);
|
||||
if (segment.empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
relativePath_ = uri.substr(maxMatchLength);
|
||||
if (relativePath_.empty() || relativePath_[0] != '/')
|
||||
if (FileUtils::isFile(curDir) && baseName_.empty())
|
||||
{
|
||||
relativePath_ = "/" + relativePath_;
|
||||
baseName_ = segment;
|
||||
}
|
||||
else if (FileUtils::isDirectory(curDir))
|
||||
{
|
||||
dir_ = FileUtils::joinPath(dir_, segment);
|
||||
}
|
||||
else if (!baseName_.empty()) // not file or dir, but we have a baseName already
|
||||
{
|
||||
pathInfo_ = FileUtils::joinPath(pathInfo_, baseName_);
|
||||
}
|
||||
}
|
||||
fullPath_ = FileUtils::joinPath(dir_, baseName_);
|
||||
}
|
||||
|
||||
std::string URIParser::getFilePath() const
|
||||
const AConfig *URIParser::getConfig() const
|
||||
{
|
||||
return root_ + relativePath_;
|
||||
}
|
||||
|
||||
std::string URIParser::getFilename() const
|
||||
{
|
||||
size_t lastSlash = relativePath_.find_last_of('/');
|
||||
if (lastSlash == std::string::npos)
|
||||
{
|
||||
return relativePath_; // No slashes, return the whole path
|
||||
}
|
||||
return relativePath_.substr(lastSlash + 1);
|
||||
}
|
||||
|
||||
std::string URIParser::getExtension() const
|
||||
{
|
||||
std::string filename = getFilename();
|
||||
size_t lastDot = filename.find_last_of('.');
|
||||
if (lastDot == std::string::npos || lastDot == 0 || lastDot == filename.length() - 1)
|
||||
{
|
||||
return ""; // No extension found or dot is at start/end
|
||||
}
|
||||
return filename.substr(lastDot + 1);
|
||||
}
|
||||
|
||||
LocationConfig const *URIParser::getLocation() const
|
||||
{
|
||||
return _locationConfig;
|
||||
return config_;
|
||||
}
|
||||
|
||||
bool URIParser::isFile() const
|
||||
{
|
||||
struct stat pathStat{};
|
||||
if (stat(getFilePath().c_str(), &pathStat) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return S_ISREG(pathStat.st_mode);
|
||||
return !baseName_.empty();
|
||||
}
|
||||
|
||||
bool URIParser::isDirectory() const
|
||||
{
|
||||
struct stat pathStat{};
|
||||
if (stat(getFilePath().c_str(), &pathStat) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return S_ISDIR(pathStat.st_mode);
|
||||
return baseName_.empty();
|
||||
}
|
||||
|
||||
bool URIParser::isValid() const
|
||||
{
|
||||
struct stat pathStat{};
|
||||
return stat(getFilePath().c_str(), &pathStat) == 0;
|
||||
return FileUtils::isValidPath(fullPath_);
|
||||
}
|
||||
|
||||
const std::string &URIParser::getBaseName() const
|
||||
{
|
||||
return baseName_;
|
||||
}
|
||||
|
||||
std::string URIParser::getExtension() const
|
||||
{
|
||||
return FileUtils::getExtension(baseName_);
|
||||
}
|
||||
|
||||
const std::string &URIParser::getFullPath() const
|
||||
{
|
||||
return fullPath_;
|
||||
}
|
||||
|
||||
const std::string &URIParser::getDir() const
|
||||
{
|
||||
return dir_;
|
||||
}
|
||||
|
||||
const std::string &URIParser::getPathInfo() const
|
||||
{
|
||||
return pathInfo_;
|
||||
}
|
||||
|
||||
const std::string &URIParser::getQuery() const
|
||||
{
|
||||
return query_;
|
||||
}
|
||||
|
||||
const std::string &URIParser::getFragment() const
|
||||
{
|
||||
return fragment_;
|
||||
}
|
||||
@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "webserv/config/AConfig.hpp"
|
||||
|
||||
#include <webserv/config/LocationConfig.hpp>
|
||||
#include <webserv/config/ServerConfig.hpp>
|
||||
#include <webserv/server/Server.hpp>
|
||||
@ -14,19 +16,31 @@ class URIParser
|
||||
public:
|
||||
URIParser(const std::string &uri, const ServerConfig &serverConfig);
|
||||
|
||||
[[nodiscard]] std::string getFilePath() const;
|
||||
[[nodiscard]] std::string getFilename() const;
|
||||
[[nodiscard]] std::string getExtension() const;
|
||||
|
||||
[[nodiscard]] const LocationConfig *getLocation() const;
|
||||
|
||||
[[nodiscard]] bool isFile() const;
|
||||
|
||||
[[nodiscard]] bool isDirectory() const;
|
||||
[[nodiscard]] bool isValid() const;
|
||||
|
||||
[[nodiscard]] std::string getExtension() const;
|
||||
[[nodiscard]] const AConfig *getConfig() const;
|
||||
[[nodiscard]] const std::string &getBaseName() const;
|
||||
[[nodiscard]] const std::string &getFullPath() const;
|
||||
[[nodiscard]] const std::string &getDir() const;
|
||||
[[nodiscard]] const std::string &getPathInfo() const;
|
||||
[[nodiscard]] const std::string &getQuery() const;
|
||||
[[nodiscard]] const std::string &getFragment() const;
|
||||
|
||||
private:
|
||||
const LocationConfig *_locationConfig;
|
||||
std::string relativePath_;
|
||||
std::string root_;
|
||||
void parseUri(const std::string &uri);
|
||||
void parseFullpath();
|
||||
|
||||
std::string uriTrimmed_;
|
||||
const AConfig *config_;
|
||||
std::string fullPath_; // dir_ + baseName_ + pathInfo_
|
||||
std::string baseName_;
|
||||
std::string dir_;
|
||||
std::string pathInfo_;
|
||||
std::string query_;
|
||||
std::string fragment_;
|
||||
|
||||
static const AConfig *matchConfig(const std::string &uri, const ServerConfig &serverConfig);
|
||||
};
|
||||
@ -1,14 +1,14 @@
|
||||
#include <webserv/router/Router.hpp>
|
||||
|
||||
#include <webserv/config/directive/ADirective.hpp>
|
||||
#include "webserv/config/AConfig.hpp"
|
||||
|
||||
#include <webserv/config/ConfigManager.hpp> // for ConfigManager
|
||||
#include <webserv/config/ServerConfig.hpp> // for ServerConfig
|
||||
#include <webserv/config/directive/ADirective.hpp>
|
||||
#include <webserv/handler/ErrorHandler.hpp> // for ErrorHandler
|
||||
#include <webserv/handler/FileHandler.hpp> // for FileHandler
|
||||
#include <webserv/handler/URIParser.hpp> // for URIParser
|
||||
#include <webserv/http/HttpHeaders.hpp> // for HttpHeaders
|
||||
#include <webserv/log/Log.hpp> // for LOCATION, Log
|
||||
#include <webserv/router/Router.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory> // for unique_ptr
|
||||
@ -18,11 +18,9 @@
|
||||
|
||||
class LocationConfig;
|
||||
|
||||
Router::Router() {}
|
||||
|
||||
bool Router::isMethodSupported(const std::string &method, const LocationConfig &location)
|
||||
bool Router::isMethodSupported(const std::string &method, const AConfig &config)
|
||||
{
|
||||
const ADirective *allowedMethods = location.getDirective("allowed_methods");
|
||||
const ADirective *allowedMethods = config.getDirective("allowed_methods");
|
||||
if (allowedMethods == nullptr || !allowedMethods->getValue().try_get<std::vector<std::string>>().has_value())
|
||||
{
|
||||
return true;
|
||||
@ -35,28 +33,24 @@ std::unique_ptr<HttpResponse> Router::handleRequest(const HttpRequest &request)
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
|
||||
ServerConfig *config =
|
||||
ServerConfig *serverConfig =
|
||||
ConfigManager::getInstance().getMatchingServerConfig(request.getHeaders().getHost().value_or(""));
|
||||
|
||||
if (config == nullptr)
|
||||
if (serverConfig == nullptr)
|
||||
{
|
||||
return ErrorHandler::getErrorResponse(400);
|
||||
}
|
||||
URIParser uriParser{request.getTarget(), *config};
|
||||
URIParser uriParser{request.getTarget(), *serverConfig};
|
||||
|
||||
const std::string &target = request.getTarget();
|
||||
static_cast<void>(target); // Suppress unused variable warning
|
||||
const std::string &method = request.getMethod();
|
||||
|
||||
const LocationConfig *location = uriParser.getLocation();
|
||||
if (location == nullptr)
|
||||
{
|
||||
return ErrorHandler::getErrorResponse(404, config);
|
||||
}
|
||||
if (!isMethodSupported(method, *location))
|
||||
const AConfig *config = uriParser.getConfig();
|
||||
if (!isMethodSupported(method, *config))
|
||||
{
|
||||
return ErrorHandler::getErrorResponse(405, config);
|
||||
}
|
||||
FileHandler fileHandler(location, uriParser);
|
||||
FileHandler fileHandler(config, uriParser);
|
||||
return fileHandler.getResponse();
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "webserv/config/AConfig.hpp"
|
||||
#include <webserv/config/LocationConfig.hpp>
|
||||
#include <webserv/http/HttpRequest.hpp> // for HttpRequest
|
||||
#include <webserv/http/HttpResponse.hpp> // for HttpResponse
|
||||
@ -13,11 +14,8 @@ class ServerConfig;
|
||||
class Router
|
||||
{
|
||||
public:
|
||||
Router();
|
||||
|
||||
[[nodiscard]] static std::unique_ptr<HttpResponse> handleRequest(const HttpRequest &request);
|
||||
|
||||
private:
|
||||
[[nodiscard]] const LocationConfig *getLocation(const std::string &path, const ServerConfig &serverConfig) const;
|
||||
[[nodiscard]] static bool isMethodSupported(const std::string &method, const LocationConfig &location);
|
||||
[[nodiscard]] static bool isMethodSupported(const std::string &method, const AConfig &config);
|
||||
};
|
||||
@ -1,6 +1,5 @@
|
||||
#include <webserv/utils/FileUtils.hpp>
|
||||
|
||||
#include <webserv/log/Log.hpp> // for Log, LOCATION
|
||||
#include <webserv/utils/FileUtils.hpp>
|
||||
|
||||
#include <cstring> // for size_t
|
||||
#include <fstream> // for basic_ifstream, basic_ios, basic_istream, ios, ifstream, operator|, basic_istream::read, basic_istream::seekg, basic_istream::tellg, streamsize
|
||||
@ -31,6 +30,12 @@ bool isFile(const std::string &path)
|
||||
return S_ISREG(pathStat.st_mode);
|
||||
}
|
||||
|
||||
bool isValidPath(const std::string &path)
|
||||
{
|
||||
struct stat pathStat{};
|
||||
return stat(path.c_str(), &pathStat) == 0;
|
||||
}
|
||||
|
||||
std::string getExtension(const std::string &filename)
|
||||
{
|
||||
size_t dotPos = filename.find_last_of('.');
|
||||
|
||||
@ -7,6 +7,7 @@ namespace FileUtils
|
||||
{
|
||||
bool isDirectory(const std::string &path);
|
||||
bool isFile(const std::string &path);
|
||||
bool isValidPath(const std::string &path);
|
||||
|
||||
std::string getExtension(const std::string &filename);
|
||||
std::string joinPath(const std::string &base, const std::string &addition);
|
||||
|
||||
@ -23,10 +23,10 @@ size_t stoul(const std::string &str, int base)
|
||||
return value;
|
||||
}
|
||||
|
||||
std::string trim(const std::string &str)
|
||||
std::string trim(const std::string &str, const std::string &charset)
|
||||
{
|
||||
size_t first = str.find_first_not_of(" \t\n\r");
|
||||
size_t last = str.find_last_not_of(" \t\n\r");
|
||||
size_t first = str.find_first_not_of(charset);
|
||||
size_t last = str.find_last_not_of(charset);
|
||||
if (first == std::string::npos || last == std::string::npos)
|
||||
{
|
||||
return "";
|
||||
@ -82,7 +82,7 @@ void removeEmptyLines(std::string &str)
|
||||
{
|
||||
if (!utils::trim(line).empty())
|
||||
{
|
||||
result += utils::trimSemi(utils::trim(line)) + '\n';
|
||||
result += utils::trim(line, " \t\n\r;") + '\n';
|
||||
}
|
||||
}
|
||||
str = result;
|
||||
|
||||
@ -7,8 +7,8 @@
|
||||
namespace utils
|
||||
{
|
||||
size_t stoul(const std::string &str, int base = 10);
|
||||
std::string trimSemi(const std::string &str);
|
||||
std::string trim(const std::string &str);
|
||||
// std::string trimSemi(const std::string &str);
|
||||
std::string trim(const std::string &str, const std::string &charset = " \t\n\r");
|
||||
size_t findCorrespondingClosingBrace(const std::string &str, size_t openPos);
|
||||
void removeEmptyLines(std::string &str);
|
||||
void removeComments(std::string &str);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user