forked from OctaForge/libostd
add command line splitting api (posix, windows) and use it from build
This commit is contained in:
parent
a191164700
commit
f01992c952
12
build.cc
12
build.cc
|
@ -11,7 +11,6 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <wordexp.h>
|
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
|
|
||||||
#include "ostd/io.hh"
|
#include "ostd/io.hh"
|
||||||
|
@ -26,6 +25,7 @@ namespace fs = ostd::filesystem;
|
||||||
|
|
||||||
/* ugly, but do not explicitly compile */
|
/* ugly, but do not explicitly compile */
|
||||||
#include "src/io.cc"
|
#include "src/io.cc"
|
||||||
|
#include "src/process.cc"
|
||||||
|
|
||||||
using strvec = std::vector<std::string>;
|
using strvec = std::vector<std::string>;
|
||||||
using pathvec = std::vector<fs::path>;
|
using pathvec = std::vector<fs::path>;
|
||||||
|
@ -145,13 +145,9 @@ static std::string get_command(std::string const &cmd, strvec const &args) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void add_args(strvec &args, std::string const &cmdl) {
|
static void add_args(strvec &args, std::string const &cmdl) {
|
||||||
wordexp_t p;
|
auto app = ostd::appender(std::move(args));
|
||||||
if (wordexp(cmdl.data(), &p, 0)) {
|
ostd::split_args(app, cmdl);
|
||||||
return;
|
args = std::move(app.get());
|
||||||
}
|
|
||||||
for (std::size_t i = 0; i < p.we_wordc; ++i) {
|
|
||||||
args.push_back(p.we_wordv[i]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static fs::path path_with_ext(fs::path const &p, fs::path const &ext) {
|
static fs::path path_with_ext(fs::path const &p, fs::path const &ext) {
|
||||||
|
|
78
ostd/process.hh
Normal file
78
ostd/process.hh
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
/** @addtogroup Utilities
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @file process.hh
|
||||||
|
*
|
||||||
|
* @brief Portable extensions to process handling.
|
||||||
|
*
|
||||||
|
* Provides POSIX and Windows abstractions for process creation and more.
|
||||||
|
*
|
||||||
|
* @copyright See COPYING.md in the project tree for further information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef OSTD_PROCESS_HH
|
||||||
|
#define OSTD_PROCESS_HH
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#include "ostd/platform.hh"
|
||||||
|
#include "ostd/string.hh"
|
||||||
|
|
||||||
|
namespace ostd {
|
||||||
|
|
||||||
|
/** @addtogroup Utilities
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct word_error: std::runtime_error {
|
||||||
|
using std::runtime_error::runtime_error;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
OSTD_EXPORT void split_args_impl(
|
||||||
|
string_range const &str, void (*func)(string_range, void *), void *data
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Splits command line argument string into individual arguments.
|
||||||
|
*
|
||||||
|
* The split is done in a platform specific manner, using wordexp on POSIX
|
||||||
|
* systems and CommandLineToArgvW on Windows. On Windows, the input string
|
||||||
|
* is assumed to be UTF-8 and the output strings are always UTF-8, on POSIX
|
||||||
|
* the wordexp implementation is followed (so it's locale specific).
|
||||||
|
*
|
||||||
|
* The `out` argument is an output range that takes a single argument at
|
||||||
|
* a time. The value type is any that can be explicitly constructed from
|
||||||
|
* an ostd::string_range. However, the string range that is used internally
|
||||||
|
* during the conversions is just temporary and freed at the end of this
|
||||||
|
* function, so it's important that the string type holds its own memory;
|
||||||
|
* an std::string will usually suffice.
|
||||||
|
*
|
||||||
|
* The ostd::word_error exception is used to handle failures of this
|
||||||
|
* function itself. It may also throw other exceptions though, particularly
|
||||||
|
* those thrown by `out` when putting the strings into it and also
|
||||||
|
* std::bad_alloc on allocation failures.
|
||||||
|
*
|
||||||
|
* @returns The forwarded `out`.
|
||||||
|
*
|
||||||
|
* @throws ostd::word_error on failure or anything thrown by `out`.
|
||||||
|
* @throws std::bad_alloc on alloation failures.
|
||||||
|
*/
|
||||||
|
template<typename OutputRange>
|
||||||
|
OutputRange &&split_args(OutputRange &&out, string_range str) {
|
||||||
|
detail::split_args_impl(str, [](string_range val, void *outp) {
|
||||||
|
static_cast<std::decay_t<OutputRange> *>(outp)->put(
|
||||||
|
range_value_t<std::decay_t<OutputRange>>{val}
|
||||||
|
);
|
||||||
|
}, &out);
|
||||||
|
return std::forward<OutputRange>(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
} /* namespace ostd */
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** @} */
|
126
src/process.cc
Normal file
126
src/process.cc
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
/* Process handling implementation bits.
|
||||||
|
*
|
||||||
|
* This file is part of libostd. See COPYING.md for futher information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <string>
|
||||||
|
#include <memory>
|
||||||
|
#include <new>
|
||||||
|
|
||||||
|
#include "ostd/process.hh"
|
||||||
|
|
||||||
|
#ifdef OSTD_PLATFORM_WIN32
|
||||||
|
# define WIN32_LEAN_AND_MEAN
|
||||||
|
# include <windows.h>
|
||||||
|
#else
|
||||||
|
# include <unistd.h>
|
||||||
|
# include <wordexp.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace ostd {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
OSTD_EXPORT void split_args_impl(
|
||||||
|
string_range const &str, void (*func)(string_range, void *), void *data
|
||||||
|
) {
|
||||||
|
#ifndef OSTD_PLATFORM_WIN32
|
||||||
|
std::string strs{str};
|
||||||
|
|
||||||
|
wordexp_t p;
|
||||||
|
if (int err; (err = wordexp(strs.data(), &p, 0))) {
|
||||||
|
switch (err) {
|
||||||
|
case WRDE_BADCHAR:
|
||||||
|
throw word_error{"illegal character"};
|
||||||
|
case WRDE_BADVAL:
|
||||||
|
/* no WRDE_UNDEF flag, won't happen */
|
||||||
|
throw word_error{"undefined shell variable"};
|
||||||
|
case WRDE_CMDSUB:
|
||||||
|
/* no WRDE_NOCMD flag, won't happen */
|
||||||
|
throw word_error{"invalid command substitution"};
|
||||||
|
case WRDE_NOSPACE:
|
||||||
|
throw std::bad_alloc{};
|
||||||
|
case WRDE_SYNTAX:
|
||||||
|
throw word_error{"syntax error"};
|
||||||
|
default:
|
||||||
|
throw word_error{"unknown error"};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if the output range throws, make sure stuff gets freed */
|
||||||
|
struct wordexp_guard {
|
||||||
|
void operator()(wordexp_t *fp) const {
|
||||||
|
wordfree(fp);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
std::unique_ptr<wordexp_t, wordexp_guard> guard{&p};
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < p.we_wordc; ++i) {
|
||||||
|
func(p.we_wordv[i], data);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
std::unique_ptr<wchar_t[]> wstr{new wchar_t[str.size() + 1]};
|
||||||
|
memset(wstr.get(), 0, (str.size() + 1) * sizeof(wchar_t));
|
||||||
|
|
||||||
|
if (!MultiByteToWideChar(
|
||||||
|
CP_UTF8, 0, str.data(), str.size(), wstr.get(), str.size() + 1
|
||||||
|
)) {
|
||||||
|
switch (GetLastError()) {
|
||||||
|
case ERROR_NO_UNICODE_TRANSLATION:
|
||||||
|
throw word_error{"unicode conversion failed"};
|
||||||
|
default:
|
||||||
|
throw word_error{"unknown error"};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int argc = 0;
|
||||||
|
wchar_t **pwargs = CommandLineToArgvW(wstr.get(), &argc);
|
||||||
|
|
||||||
|
guard.reset());
|
||||||
|
if (!pwargs) {
|
||||||
|
throw word_error{"command line parsing failed"};
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if anything throws, make sure stuff gets freed */
|
||||||
|
struct wchar_guard {
|
||||||
|
void operator()(wchar_t **p) const {
|
||||||
|
LocalFree(p);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
std::unique_ptr<wchar_t *, wchar_guard> wguard{pwargs};
|
||||||
|
|
||||||
|
for (int i = 0; i < argc; ++i) {
|
||||||
|
wchar_t *arg = pwargs[i];
|
||||||
|
std::size_t arglen = wcslen(arg);
|
||||||
|
|
||||||
|
std::size_t req = 0;
|
||||||
|
if (!(req = WideCharToMultiByte(
|
||||||
|
CP_UTF8, 0, arg, arglen, nullptr, 0, nullptr, nullptr
|
||||||
|
))) {
|
||||||
|
switch (GetLastError()) {
|
||||||
|
case ERROR_NO_UNICODE_TRANSLATION:
|
||||||
|
throw word_error{"unicode conversion failed"};
|
||||||
|
default:
|
||||||
|
throw word_error{"unknown error"};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<char[]> buf{new char[req]};
|
||||||
|
if (!WideCharToMultiByte(
|
||||||
|
CP_UTF8, 0, arg, arglen, buf.get(), req, nullptr, nullptr
|
||||||
|
)) {
|
||||||
|
switch (GetLastError()) {
|
||||||
|
case ERROR_NO_UNICODE_TRANSLATION:
|
||||||
|
throw word_error{"unicode conversion failed"};
|
||||||
|
default:
|
||||||
|
throw word_error{"unknown error"};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func(string_range{buf.get(), buf.get() + req - 1}, data);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
} /* namespace detail */
|
||||||
|
} /* namespace ostd */
|
Loading…
Reference in a new issue