drop libostd requirement entirely
parent
cb926a5750
commit
82d366366e
28
README.md
28
README.md
|
@ -27,8 +27,8 @@ advantages, including:
|
|||
|
||||
* Independent implementation (can be embedded in any project)
|
||||
* No global state (multiple CubeScripts in a single program)
|
||||
* Modern C++17 API (no macros, use of strongly typed enums, lambdas, ranges etc.)
|
||||
* C++17 lambdas can be used as commands (including captures and type inference)
|
||||
* Modern C++20 API
|
||||
* C++ lambdas can be used as commands (including captures and type inference)
|
||||
* Error handling including recovery (protected call system similar to Lua)
|
||||
* Stricter parsing (strings cannot be left unfinished etc.)
|
||||
* Loop control statements (`break` and `continue`)
|
||||
|
@ -70,13 +70,8 @@ utilized in the outside native code.
|
|||
|
||||
## Building and usage
|
||||
|
||||
The only dependency is libostd:
|
||||
|
||||
https://git.octaforge.org/OctaForge/libostd
|
||||
|
||||
https://github.com/OctaForge/libostd
|
||||
|
||||
If libostd can work on your system, so can libcubescript.
|
||||
There are no dependencies (other than a suitable compiler and the standard
|
||||
library).
|
||||
|
||||
Libostd is built using Meson. Therefore, you need to install Meson and then
|
||||
you can compile it as usual. Typically, this will be something like
|
||||
|
@ -84,16 +79,17 @@ you can compile it as usual. Typically, this will be something like
|
|||
~~~
|
||||
mkdir build && cd build
|
||||
meson ..
|
||||
meson compile
|
||||
ninja all
|
||||
~~~
|
||||
|
||||
Link the libcubescript library together with your application and everything should just work.
|
||||
It also builds the REPL.
|
||||
Link the libcubescript library together with your application and everything
|
||||
should just work. It also builds the REPL.
|
||||
|
||||
The project also bundles the linenoise line editing library which has been modified
|
||||
to compile cleanly as C++ (with the same flags as libcubescript). It's used strictly
|
||||
for the REPL only (you don't need it to build libcubescript itself). The version
|
||||
in the repository tracks Git revision https://github.com/antirez/linenoise/commit/c894b9e59f02203dbe4e2be657572cf88c4230c3.
|
||||
The project also bundles the linenoise line editing library which has been
|
||||
modified to compile cleanly as C++ (with the same flags as libcubescript).
|
||||
It's used strictly for the REPL only (you don't need it to build libcubescript
|
||||
itself). The version in the repository tracks Git revision
|
||||
https://github.com/antirez/linenoise/commit/c894b9e59f02203dbe4e2be657572cf88c4230c3.
|
||||
|
||||
## Licensing
|
||||
|
||||
|
|
|
@ -598,12 +598,12 @@ struct cs_error {
|
|||
}
|
||||
|
||||
template<typename ...A>
|
||||
cs_error(cs_state &cs, std::string_view msg, A &&...args):
|
||||
cs_error(cs_state &cs, std::string_view msg, A const &...args):
|
||||
p_errmsg(), p_stack(cs)
|
||||
{
|
||||
char fbuf[512];
|
||||
int written = std::snprintf(
|
||||
fbuf, sizeof(fbuf), "%.*s", int(msg.size()), msg.data()
|
||||
fbuf, sizeof(fbuf), msg.data(), args...
|
||||
);
|
||||
if (written >= int(sizeof(fbuf))) {
|
||||
written = std::strlen(fbuf);
|
||||
|
|
14
src/cs_vm.cc
14
src/cs_vm.cc
|
@ -110,8 +110,8 @@ cs_stack_state cs_error::save_stack(cs_state &cs) {
|
|||
std::string_view cs_error::save_msg(
|
||||
cs_state &cs, std::string_view msg
|
||||
) {
|
||||
if (msg.size() > sizeof(cs.p_errbuf)) {
|
||||
msg = msg.substr(0, sizeof(cs.p_errbuf));
|
||||
if (msg.size() >= sizeof(cs.p_errbuf)) {
|
||||
msg = msg.substr(0, sizeof(cs.p_errbuf) - 1);
|
||||
}
|
||||
cs_gen_state *gs = cs.p_pstate;
|
||||
if (gs) {
|
||||
|
@ -137,6 +137,7 @@ std::string_view cs_error::save_msg(
|
|||
return std::string_view{cs.p_errbuf, std::size_t(sz)};
|
||||
}
|
||||
memcpy(cs.p_errbuf, msg.data(), msg.size());
|
||||
cs.p_errbuf[msg.size()] = '\0';
|
||||
return std::string_view{cs.p_errbuf, msg.size()};
|
||||
}
|
||||
|
||||
|
@ -527,7 +528,7 @@ struct RunDepthRef {
|
|||
static inline cs_alias *cs_get_lookup_id(cs_state &cs, uint32_t op) {
|
||||
cs_ident *id = cs.p_state->identmap[op >> 8];
|
||||
if (id->get_flags() & CS_IDF_UNKNOWN) {
|
||||
throw cs_error(cs, "unknown alias lookup: %s", id->get_name());
|
||||
throw cs_error(cs, "unknown alias lookup: %s", id->get_name().data());
|
||||
}
|
||||
return static_cast<cs_alias *>(id);
|
||||
}
|
||||
|
@ -574,7 +575,7 @@ static inline int cs_get_lookupu_type(
|
|||
return CsIdUnknown;
|
||||
}
|
||||
}
|
||||
throw cs_error(cs, "unknown alias lookup: %s", arg.get_str());
|
||||
throw cs_error(cs, "unknown alias lookup: %s", arg.get_str().data());
|
||||
}
|
||||
|
||||
static uint32_t *runcode(cs_state &cs, uint32_t *code, cs_value &result) {
|
||||
|
@ -1421,7 +1422,7 @@ static uint32_t *runcode(cs_state &cs, uint32_t *code, cs_value &result) {
|
|||
if (id->get_flags() & CS_IDF_UNKNOWN) {
|
||||
force_arg(result, op & CS_CODE_RET_MASK);
|
||||
throw cs_error(
|
||||
cs, "unknown command: %s", id->get_name()
|
||||
cs, "unknown command: %s", id->get_name().data()
|
||||
);
|
||||
}
|
||||
cs_call_alias(
|
||||
|
@ -1471,8 +1472,9 @@ noid:
|
|||
}
|
||||
result.force_none();
|
||||
force_arg(result, op & CS_CODE_RET_MASK);
|
||||
std::string_view ids{idn};
|
||||
throw cs_error(
|
||||
cs, "unknown command: %s", std::string_view{idn}
|
||||
cs, "unknown command: %s", ids.data()
|
||||
);
|
||||
}
|
||||
result.force_none();
|
||||
|
|
|
@ -474,7 +474,7 @@ LIBCUBESCRIPT_EXPORT cs_ident *cs_state::new_ident(std::string_view name, int fl
|
|||
if (!id) {
|
||||
if (cs_check_num(name)) {
|
||||
throw cs_error(
|
||||
*this, "number %s is not a valid identifier name", name
|
||||
*this, "number %s is not a valid identifier name", name.data()
|
||||
);
|
||||
}
|
||||
id = add_ident(p_state->create<cs_alias>(
|
||||
|
@ -560,10 +560,10 @@ LIBCUBESCRIPT_EXPORT cs_svar *cs_state::new_svar(
|
|||
LIBCUBESCRIPT_EXPORT void cs_state::reset_var(std::string_view name) {
|
||||
cs_ident *id = get_ident(name);
|
||||
if (!id) {
|
||||
throw cs_error(*this, "variable %s does not exist", name);
|
||||
throw cs_error(*this, "variable %s does not exist", name.data());
|
||||
}
|
||||
if (id->get_flags() & CS_IDF_READONLY) {
|
||||
throw cs_error(*this, "variable %s is read only", name);
|
||||
throw cs_error(*this, "variable %s is read only", name.data());
|
||||
}
|
||||
clear_override(*id);
|
||||
}
|
||||
|
@ -600,11 +600,11 @@ LIBCUBESCRIPT_EXPORT void cs_state::set_alias(std::string_view name, cs_value v)
|
|||
default:
|
||||
throw cs_error(
|
||||
*this, "cannot redefine builtin %s with an alias",
|
||||
id->get_name()
|
||||
id->get_name().data()
|
||||
);
|
||||
}
|
||||
} else if (cs_check_num(name)) {
|
||||
throw cs_error(*this, "cannot alias number %s", name);
|
||||
throw cs_error(*this, "cannot alias number %s", name.data());
|
||||
} else {
|
||||
add_ident(p_state->create<cs_alias>(
|
||||
*this, cs_strref{*p_state, name}, std::move(v), identflags
|
||||
|
@ -657,7 +657,8 @@ static inline void cs_override_var(cs_state &cs, cs_var *v, int &vflags, SF sf)
|
|||
if ((cs.identflags & CS_IDF_OVERRIDDEN) || (vflags & CS_IDF_OVERRIDE)) {
|
||||
if (vflags & CS_IDF_PERSIST) {
|
||||
throw cs_error(
|
||||
cs, "cannot override persistent variable '%s'", v->get_name()
|
||||
cs, "cannot override persistent variable '%s'",
|
||||
v->get_name().data()
|
||||
);
|
||||
}
|
||||
if (!(vflags & CS_IDF_OVERRIDDEN)) {
|
||||
|
@ -825,14 +826,14 @@ cs_int cs_clamp_var(cs_state &cs, cs_ivar *iv, cs_int v) {
|
|||
: "valid range for '%s' is 0x%X..0x%X"
|
||||
)
|
||||
: "valid range for '%s' is %d..%d",
|
||||
iv->get_name(), iv->get_val_min(), iv->get_val_max()
|
||||
iv->get_name().data(), iv->get_val_min(), iv->get_val_max()
|
||||
);
|
||||
}
|
||||
|
||||
LIBCUBESCRIPT_EXPORT void cs_state::set_var_int_checked(cs_ivar *iv, cs_int v) {
|
||||
if (iv->get_flags() & CS_IDF_READONLY) {
|
||||
throw cs_error(
|
||||
*this, "variable '%s' is read only", iv->get_name()
|
||||
*this, "variable '%s' is read only", iv->get_name().data()
|
||||
);
|
||||
}
|
||||
cs_override_var(
|
||||
|
@ -871,7 +872,7 @@ cs_float cs_clamp_fvar(cs_state &cs, cs_fvar *fv, cs_float v) {
|
|||
vmin.set_float(fv->get_val_min());
|
||||
vmax.set_float(fv->get_val_max());
|
||||
throw cs_error(
|
||||
cs, "valid range for '%s' is %s..%s", fv->get_name(),
|
||||
cs, "valid range for '%s' is %s..%s", fv->get_name().data(),
|
||||
vmin.force_str(), vmax.force_str()
|
||||
);
|
||||
return v;
|
||||
|
@ -880,7 +881,7 @@ cs_float cs_clamp_fvar(cs_state &cs, cs_fvar *fv, cs_float v) {
|
|||
LIBCUBESCRIPT_EXPORT void cs_state::set_var_float_checked(cs_fvar *fv, cs_float v) {
|
||||
if (fv->get_flags() & CS_IDF_READONLY) {
|
||||
throw cs_error(
|
||||
*this, "variable '%s' is read only", fv->get_name()
|
||||
*this, "variable '%s' is read only", fv->get_name().data()
|
||||
);
|
||||
}
|
||||
cs_override_var(
|
||||
|
@ -899,7 +900,7 @@ LIBCUBESCRIPT_EXPORT void cs_state::set_var_str_checked(
|
|||
) {
|
||||
if (sv->get_flags() & CS_IDF_READONLY) {
|
||||
throw cs_error(
|
||||
*this, "variable '%s' is read only", sv->get_name()
|
||||
*this, "variable '%s' is read only", sv->get_name().data()
|
||||
);
|
||||
}
|
||||
cs_override_var(
|
||||
|
|
|
@ -14,11 +14,8 @@ libcubescript_src = [
|
|||
'lib_str.cc'
|
||||
]
|
||||
|
||||
libostd_dep = dependency('libostd', fallback: ['libostd', 'libostd_static'])
|
||||
|
||||
libcubescript_lib = both_libraries('cubescript',
|
||||
libcubescript_src,
|
||||
dependencies: libostd_dep,
|
||||
include_directories: libcubescript_includes + [include_directories('.')],
|
||||
cpp_args: extra_cxxflags,
|
||||
install: true,
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
[wrap-git]
|
||||
directory = libostd
|
||||
url = https://git.octaforge.org/OctaForge/libostd.git
|
||||
revision = head
|
|
@ -2,15 +2,23 @@
|
|||
/* use nothing (no line editing support) */
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include <ostd/string.hh>
|
||||
#include <string>
|
||||
|
||||
inline void init_lineedit(cs_state &, std::string_view) {
|
||||
}
|
||||
|
||||
inline std::optional<std::string> read_line(cs_state &, cs_svar *pr) {
|
||||
ostd::write(pr->get_value());
|
||||
return ostd::cin.get_line(ostd::appender<std::string>()).get();
|
||||
std::string lbuf;
|
||||
char buf[512];
|
||||
printf("%s", pr->get_value().data());
|
||||
std::fflush(stdout);
|
||||
while (fgets(buf, sizeof(buf), stdin)) {
|
||||
lbuf += static_cast<char const *>(buf);
|
||||
if (strchr(buf, '\n')) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return std::move(lbuf);
|
||||
}
|
||||
|
||||
inline void add_history(cs_state &, std::string_view) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#ifdef CS_REPL_USE_LINENOISE
|
||||
#ifdef OSTD_PLATFORM_POSIX
|
||||
#ifndef _WIN32
|
||||
#ifndef CS_REPL_HAS_EDIT
|
||||
#define CS_REPL_HAS_EDIT
|
||||
/* use the bundled linenoise library, default */
|
||||
|
@ -10,8 +10,6 @@
|
|||
|
||||
#include <optional>
|
||||
|
||||
#include <ostd/string.hh>
|
||||
|
||||
#include "linenoise.hh"
|
||||
|
||||
static cs_state *ln_cs = nullptr;
|
||||
|
|
|
@ -3,12 +3,9 @@
|
|||
#define CS_REPL_HAS_EDIT
|
||||
/* use the GNU readline library */
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <optional>
|
||||
|
||||
#include <ostd/string.hh>
|
||||
|
||||
#include <readline/readline.h>
|
||||
#include <readline/history.h>
|
||||
|
||||
|
@ -16,15 +13,14 @@ static cs_state *rd_cs = nullptr;
|
|||
|
||||
inline char *ln_complete_list(char const *buf, int state) {
|
||||
static std::string_view cmd;
|
||||
static ostd::iterator_range<cs_ident **> itr;
|
||||
static std::span<cs_ident *> itr;
|
||||
|
||||
if (!state) {
|
||||
cmd = get_complete_cmd(buf);
|
||||
itr = rd_cs->get_idents();
|
||||
}
|
||||
|
||||
for (; !itr.empty(); itr.pop_front()) {
|
||||
cs_ident *id = itr.front();
|
||||
for (cs_ident *id: itr) {
|
||||
if (!id->is_command()) {
|
||||
continue;
|
||||
}
|
||||
|
@ -33,7 +29,7 @@ inline char *ln_complete_list(char const *buf, int state) {
|
|||
continue;
|
||||
}
|
||||
if (idname.substr(0, cmd.size()) == cmd) {
|
||||
itr.pop_front();
|
||||
++itr;
|
||||
return strdup(idname.data());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ repl_src = [
|
|||
'repl.cc'
|
||||
]
|
||||
|
||||
repl_deps = [libostd_dep, libcubescript]
|
||||
repl_deps = [libcubescript]
|
||||
repl_flags = []
|
||||
|
||||
if get_option('readline')
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
#include <signal.h>
|
||||
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
#include <optional>
|
||||
#include <memory>
|
||||
#include <iterator>
|
||||
|
||||
#include <ostd/platform.hh>
|
||||
#include <ostd/io.hh>
|
||||
#include <ostd/string.hh>
|
||||
|
||||
#include <cubescript/cubescript.hh>
|
||||
|
||||
using namespace cscript;
|
||||
|
@ -16,7 +15,7 @@ std::string_view version = "CubeScript 0.0.1";
|
|||
|
||||
/* util */
|
||||
|
||||
#ifdef OSTD_PLATFORM_WIN32
|
||||
#if defined(_WIN32)
|
||||
#include <io.h>
|
||||
static bool stdin_is_tty() {
|
||||
return _isatty(_fileno(stdin));
|
||||
|
@ -165,9 +164,9 @@ inline cs_command *get_hint_cmd(cs_state &cs, std::string_view buf) {
|
|||
/* usage */
|
||||
|
||||
void print_usage(std::string_view progname, bool err) {
|
||||
auto &s = err ? ostd::cerr : ostd::cout;
|
||||
s.writeln(
|
||||
"Usage: ", progname, " [options] [file]\n"
|
||||
std::fprintf(
|
||||
err ? stderr : stdout,
|
||||
"Usage: %s [options] [file]\n"
|
||||
"Options:\n"
|
||||
" -e str run string \"str\"\n"
|
||||
" -i enter interactive mode after the above\n"
|
||||
|
@ -175,12 +174,13 @@ void print_usage(std::string_view progname, bool err) {
|
|||
" -h show this message\n"
|
||||
" -- stop handling options\n"
|
||||
" - execute stdin and stop handling options"
|
||||
"\n",
|
||||
progname.data()
|
||||
);
|
||||
s.flush();
|
||||
}
|
||||
|
||||
void print_version() {
|
||||
ostd::writeln(version);
|
||||
printf("%s\n", version.data());
|
||||
}
|
||||
|
||||
static cs_state *scs = nullptr;
|
||||
|
@ -200,33 +200,35 @@ static void repl_print_var(cs_state const &cs, cs_var const &var) {
|
|||
auto &iv = static_cast<cs_ivar const &>(var);
|
||||
auto val = iv.get_value();
|
||||
if (!(iv.get_flags() & CS_IDF_HEX) || (val < 0)) {
|
||||
ostd::writefln("%s = %d", iv.get_name(), val);
|
||||
std::printf("%s = %d\n", iv.get_name().data(), val);
|
||||
} else if (iv.get_val_max() == 0xFFFFFF) {
|
||||
ostd::writefln(
|
||||
"%s = 0x%.6X (%d, %d, %d)", iv.get_name(),
|
||||
std::printf(
|
||||
"%s = 0x%.6X (%d, %d, %d)\n",
|
||||
iv.get_name().data(),
|
||||
val, (val >> 16) & 0xFF, (val >> 8) & 0xFF, val & 0xFF
|
||||
);
|
||||
} else {
|
||||
ostd::writefln("%s = 0x%X", iv.get_name(), val);
|
||||
std::printf("%s = 0x%X\n", iv.get_name().data(), val);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case cs_ident_type::FVAR: {
|
||||
auto &fv = static_cast<cs_fvar const &>(var);
|
||||
auto val = fv.get_value();
|
||||
ostd::writefln(
|
||||
(floor(val) == val) ? "%s = %.1f" : "%s = %.7g",
|
||||
fv.get_name(), val
|
||||
);
|
||||
if (std::floor(val) == val) {
|
||||
std::printf("%s = %.1f", fv.get_name().data(), val);
|
||||
} else {
|
||||
std::printf("%s = %.7g", fv.get_name().data(), val);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case cs_ident_type::SVAR: {
|
||||
auto &sv = static_cast<cs_svar const &>(var);
|
||||
auto val = std::string_view{sv.get_value()};
|
||||
if (val.find('"') == val.npos) {
|
||||
ostd::writefln("%s = \"%s\"", sv.get_name(), val);
|
||||
std::printf("%s = \"%s\"", sv.get_name().data(), val.data());
|
||||
} else {
|
||||
ostd::writefln("%s = [%s]", sv.get_name(), val);
|
||||
std::printf("%s = [%s]", sv.get_name().data(), val.data());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -236,28 +238,29 @@ static void repl_print_var(cs_state const &cs, cs_var const &var) {
|
|||
}
|
||||
|
||||
static bool do_run_file(cs_state &cs, std::string_view fname, cs_value &ret) {
|
||||
std::unique_ptr<char[]> buf;
|
||||
std::size_t len;
|
||||
|
||||
ostd::file_stream f{fname, ostd::stream_mode::READ};
|
||||
if (!f.is_open()) {
|
||||
FILE *f = std::fopen(fname.data(), "rb");
|
||||
if (!f) {
|
||||
return false;
|
||||
}
|
||||
|
||||
len = f.size();
|
||||
buf = std::make_unique<char[]>(len + 1);
|
||||
std::fseek(f, 0, SEEK_END);
|
||||
auto len = std::ftell(f);
|
||||
std::fseek(f, 0, SEEK_SET);
|
||||
|
||||
auto buf = std::make_unique<char[]>(len + 1);
|
||||
if (!buf) {
|
||||
std::fclose(f);
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
f.get(buf.get(), len);
|
||||
} catch (...) {
|
||||
if (std::fread(buf.get(), 1, len, f) != std::size_t(len)) {
|
||||
std::fclose(f);
|
||||
return false;
|
||||
}
|
||||
|
||||
buf[len] = '\0';
|
||||
|
||||
cs.run(std::string_view{buf.get(), len}, ret, fname);
|
||||
cs.run(std::string_view{buf.get(), std::size_t(len)}, ret, fname);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -268,7 +271,7 @@ static bool do_call(cs_state &cs, std::string_view line, bool file = false) {
|
|||
try {
|
||||
if (file) {
|
||||
if (!do_run_file(cs, line, ret)) {
|
||||
ostd::cerr.writeln("cannot read file: ", line);
|
||||
std::fprintf(stderr, "cannot read file: %s\n", line.data());
|
||||
}
|
||||
} else {
|
||||
cs.run(line, ret);
|
||||
|
@ -291,18 +294,20 @@ static bool do_call(cs_state &cs, std::string_view line, bool file = false) {
|
|||
if (!file && ((terr == "missing \"]\"") || (terr == "missing \")\""))) {
|
||||
return true;
|
||||
}
|
||||
ostd::writeln(!is_lnum ? "stdin: " : "stdin:", e.what());
|
||||
std::printf(
|
||||
"%s%s\n", !is_lnum ? "stdin: " : "stdin:", e.what().data()
|
||||
);
|
||||
if (e.get_stack().get()) {
|
||||
std::string str;
|
||||
cscript::util::print_stack(std::back_inserter(str), e.get_stack());
|
||||
ostd::writeln(str);
|
||||
std::printf("%s\n", str.data());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
signal(SIGINT, SIG_DFL);
|
||||
scs = nullptr;
|
||||
if (ret.get_type() != cs_value_type::NONE) {
|
||||
ostd::writeln(std::string_view{ret.get_str()});
|
||||
std::printf("%s\n", std::string_view{ret.get_str()}.data());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -316,7 +321,7 @@ static void do_tty(cs_state &cs) {
|
|||
do_exit = true;
|
||||
});
|
||||
|
||||
ostd::writeln(version, " (REPL mode)");
|
||||
std::printf("%s (REPL mode)\n", version.data());
|
||||
for (;;) {
|
||||
auto line = read_line(cs, prompt);
|
||||
if (!line) {
|
||||
|
@ -364,7 +369,7 @@ int main(int argc, char **argv) {
|
|||
});
|
||||
|
||||
gcs.new_command("echo", "C", [](auto &, auto args, auto &) {
|
||||
ostd::writeln(std::string_view{args[0].get_str()});
|
||||
std::printf("%s\n", std::string_view{args[0].get_str()}.data());
|
||||
});
|
||||
|
||||
int firstarg = 0;
|
||||
|
@ -450,7 +455,7 @@ endargs:
|
|||
return 0;
|
||||
} else {
|
||||
std::string str;
|
||||
for (signed char c = '\0'; (c = ostd::cin.get_char()) != EOF;) {
|
||||
for (signed char c = '\0'; (c = std::fgetc(stdin)) != EOF;) {
|
||||
str += c;
|
||||
}
|
||||
do_call(gcs, str);
|
||||
|
|
Loading…
Reference in New Issue