Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ An implementation of [openkal](https://github.com/mcpplibs/openkal) for Windows.

```toml
[dependencies]
openkal = "0.17.0"
openkal = "0.17.1"

[target.'cfg(windows)'.dependencies]
openkal-windows = "0.13.0"
openkal-windows = "0.13.1"
```

Its purpose is as much to test the specification as to be used. openkal was
Expand Down
4 changes: 2 additions & 2 deletions mcpp.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
namespace = "mcpplibs"
name = "openkal-windows"
version = "0.13.0"
version = "0.13.1"
description = "An implementation of openkal for Windows, written on the Win32 interfaces and the object manager beneath them, using no C runtime symbol."
license = "Apache-2.0"

Expand Down Expand Up @@ -62,7 +62,7 @@ provides-interfaces = [
]

[dependencies]
openkal = "0.17.0"
openkal = "0.17.1"

# The package contributes definitions and no modules. The interface it
# implements is declared by the specification package, which this package
Expand Down
14 changes: 11 additions & 3 deletions src/process.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,9 @@ int kal_process_spawn(const kal_spawn* how,
if (!dir || !run) return kal_err_invalid;
if (!okw::acceptable(path, path_len)) return kal_err_invalid;
if (how->grant_count > 0) return kal_err_not_supported;
if ((how->flags & ~KAL_SPAWN_BOUND_LIFETIME) != 0) return kal_err_not_supported;
if ((how->flags & ~(KAL_SPAWN_BOUND_LIFETIME | KAL_SPAWN_ONE_STRING)) != 0) return kal_err_not_supported;
// Clause 7.6, 0.17.1: the elements after the first joined as they are.
const bool one_string = (how->flags & KAL_SPAWN_ONE_STRING) != 0;
if (envc > 0 && (envp == nullptr || envp_lens == nullptr)) return kal_err_invalid;
if (!entries_well_formed(envp, envp_lens, envc)) return kal_err_invalid;
const bool bound = (how->flags & KAL_SPAWN_BOUND_LIFETIME) != 0;
Expand Down Expand Up @@ -421,8 +423,13 @@ int kal_process_spawn(const kal_spawn* how,
const okw_uptr room = kCommandLine - 1;
if (!append_utf8(one, room, produced, argv[i], argv_lens[i]))
return argv_lens[i] >= room ? kal_err_no_space : kal_err_invalid;
if (!append_quoted(line, kCommandLine, used, one, produced))
if (one_string && i > 0) {
// KAL_SPAWN_ONE_STRING: the program parses this string by rules of
// its own (the command interpreter's /c), so it is passed as it is.
if (!append_wide(line, kCommandLine, used, one, produced)) return kal_err_no_space;
} else if (!append_quoted(line, kCommandLine, used, one, produced)) {
return kal_err_no_space;
}
}
line[used] = 0;
if (argc == 0) line[0] = 0;
Expand Down Expand Up @@ -796,6 +803,7 @@ kal_uintptr kal_process_props(void) { return
| KAL_PROCESS_PROP_JOB
| KAL_PROCESS_PROP_BOUND_LIFETIME // K5: a job that ends with this program
| KAL_PROCESS_PROP_STOP_REQUESTED // K3: the routine handles, and the word is read
| KAL_PROCESS_PROP_OPEN_ID; }
| KAL_PROCESS_PROP_OPEN_ID
| KAL_PROCESS_PROP_ONE_STRING; } // 0.17.1: a program here receives one string

}
82 changes: 82 additions & 0 deletions tests/one_string_command_line.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// The vector joined as it is, where a program receives one string (SPEC.md
// clause 7.6, version 0.17.1, KAL_SPAWN_ONE_STRING).
//
// The command interpreter reads the rest of its command line after `/s /c' as a
// command of its own grammar, with the outer pair of quotation marks removed. A
// command holding quotation marks --- a path with a space in it --- could not be
// given to it through the quoting that recovers a vector: each inner mark
// arrived escaped with a backslash, which the interpreter does not read. With
// the flag the command arrives as written, and its output says so.
#include <cstdio>
#include <cstring>
#include <openkal/fs.h>
#include <openkal/process.h>
#include <openkal/stream.h>
#include <openkal/types.h>

namespace {

int failures = 0;

void check(bool held, const char* what) {
if (held) { std::printf("ok: %s\n", what); return; }
std::printf("FAIL: %s\n", what);
++failures;
}

bool system_volume(kal_dir* volume) {
const kal_uintptr count = kal_fs_preopen_count();
for (kal_uintptr i = 0; i < count; ++i) {
char name[16]{};
kal_uintptr len = 0;
kal_dir d{};
if (kal_fs_preopen(i, &d, name, sizeof name, &len) != kal_ok) continue;
if (len == 2 && (name[0] | 0x20) == 'c' && name[1] == ':') { *volume = d; return true; }
}
return false;
}

// Runs the interpreter with `command' after `/d /s /c' and returns what it wrote.
int run(kal_uintptr flags, const char* command, char* out, kal_uintptr cap, kal_uintptr* got) {
kal_dir volume{};
if (!system_volume(&volume)) return -1;
const char* path = "Windows/System32/cmd.exe";
const char* argv[5] = { "cmd.exe", "/d", "/s", "/c", command };
const kal_uintptr lens[5] = { 7, 2, 2, 2, std::strlen(command) };
kal_stream mine{}, theirs{};
if (kal_process_channel(&mine, &theirs) != kal_ok) return -1;
const kal_spawn how{ volume, volume, nullptr, nullptr, 0, flags };
const kal_spawn_streams streams{ kal_stream{}, theirs, theirs };
kal_process p{};
const int rc = kal_process_spawn(&how, path, std::strlen(path), argv, lens, 5,
nullptr, nullptr, 0, &streams, &p);
kal_process_channel_close(theirs);
if (rc != kal_ok) { kal_process_channel_close(mine); return rc; }
*got = 0;
for (;;) {
const kal_intptr n = kal_stream_read(mine, out + *got, cap - 1 - *got);
if (n <= 0) break;
*got += static_cast<kal_uintptr>(n);
}
out[*got] = '\0';
int status = 0, terminated = 0;
kal_process_wait(p, &status, &terminated);
kal_process_close(p);
kal_process_channel_close(mine);
return 0;
}

} // namespace

int main() {
check((kal_process_props() & KAL_PROCESS_PROP_ONE_STRING) != 0, "the position is claimed");
char out[4096];
kal_uintptr got = 0;
const char* command = "\"echo \"a b\" && echo c\"";
const int rc = run(KAL_SPAWN_ONE_STRING, command, out, sizeof out, &got);
if (rc == -1) { std::printf("skip: no C: volume among the preopens\n"); return 0; }
check(rc == kal_ok, "the interpreter starts with the command line joined as it is");
check(std::strstr(out, "\"a b\"") != nullptr && std::strstr(out, "c") != nullptr,
"and runs the command as written, quotation marks included");
return failures;
}
Loading