koffi 2.14.1 → 2.15.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -0
- package/build/koffi/darwin_arm64/koffi.node +0 -0
- package/build/koffi/darwin_x64/koffi.node +0 -0
- package/build/koffi/freebsd_arm64/koffi.node +0 -0
- package/build/koffi/freebsd_ia32/koffi.node +0 -0
- package/build/koffi/freebsd_x64/koffi.node +0 -0
- package/build/koffi/linux_arm64/koffi.node +0 -0
- package/build/koffi/linux_armhf/koffi.node +0 -0
- package/build/koffi/linux_ia32/koffi.node +0 -0
- package/build/koffi/linux_loong64/koffi.node +0 -0
- package/build/koffi/linux_riscv64d/koffi.node +0 -0
- package/build/koffi/linux_x64/koffi.node +0 -0
- package/build/koffi/musl_arm64/koffi.node +0 -0
- package/build/koffi/musl_x64/koffi.node +0 -0
- package/build/koffi/openbsd_ia32/koffi.node +0 -0
- package/build/koffi/openbsd_x64/koffi.node +0 -0
- package/build/koffi/win32_arm64/koffi.node +0 -0
- package/build/koffi/win32_ia32/koffi.node +0 -0
- package/build/koffi/win32_x64/koffi.node +0 -0
- package/doc/assets.ini +2 -1
- package/doc/build.sh +9 -0
- package/doc/pages/404.md +17 -0
- package/doc/pages/index.md +5 -3
- package/doc/pages/misc.md +18 -11
- package/doc/pages.ini +4 -0
- package/doc/static/highlight.js +2 -14
- package/doc/static/koffi.css +3 -15
- package/doc/static/print.css +2 -14
- package/doc/templates/code.html +1 -2
- package/doc/templates/page.html +1 -2
- package/index.d.ts +29 -24
- package/index.js +9 -9
- package/indirect.js +9 -9
- package/{src/core → lib/native}/base/base.cc +1137 -674
- package/{src/core → lib/native}/base/base.hh +362 -195
- package/{src/core → lib/native}/base/crc.inc +2 -20
- package/lib/native/base/crc_gen.py +72 -0
- package/{src/core → lib/native}/base/mimetypes.inc +2 -20
- package/{src/core → lib/native}/base/mimetypes_gen.py +2 -21
- package/lib/native/base/tower.cc +821 -0
- package/lib/native/base/tower.hh +81 -0
- package/{src/core → lib/native}/base/unicode.inc +2 -20
- package/{src/core → lib/native}/base/unicode_gen.py +4 -41
- package/package.json +2 -2
- package/src/cnoke/assets/FindCNoke.cmake +24 -30
- package/src/cnoke/assets/win_delay_hook.c +6 -20
- package/src/cnoke/cnoke.js +2 -21
- package/src/cnoke/src/builder.js +51 -66
- package/src/cnoke/src/index.js +2 -20
- package/src/cnoke/src/tools.js +2 -20
- package/src/koffi/CMakeLists.txt +30 -23
- package/src/koffi/cmake/raylib.cmake +5 -22
- package/src/koffi/cmake/sqlite3.cmake +2 -20
- package/src/koffi/src/abi_arm32.cc +7 -25
- package/src/koffi/src/abi_arm32_asm.S +2 -20
- package/src/koffi/src/abi_arm64.cc +7 -25
- package/src/koffi/src/abi_arm64_asm.S +2 -20
- package/src/koffi/src/abi_arm64_asm.asm +2 -20
- package/src/koffi/src/abi_loong64.cc +2 -20
- package/src/koffi/src/abi_loong64_asm.S +2 -20
- package/src/koffi/src/abi_riscv64.cc +7 -25
- package/src/koffi/src/abi_riscv64_asm.S +2 -20
- package/src/koffi/src/abi_x64_sysv.cc +7 -25
- package/src/koffi/src/abi_x64_sysv_asm.S +2 -20
- package/src/koffi/src/abi_x64_win.cc +12 -30
- package/src/koffi/src/abi_x64_win_asm.S +162 -0
- package/src/koffi/src/abi_x64_win_asm.asm +2 -20
- package/src/koffi/src/abi_x86.cc +7 -25
- package/src/koffi/src/abi_x86_asm.S +2 -20
- package/src/koffi/src/abi_x86_asm.asm +2 -20
- package/src/koffi/src/call.cc +25 -45
- package/src/koffi/src/call.hh +3 -21
- package/src/koffi/src/errno.inc +2 -20
- package/src/koffi/src/ffi.cc +64 -63
- package/src/koffi/src/ffi.hh +15 -30
- package/src/koffi/src/init.js +2 -20
- package/src/koffi/src/parser.cc +13 -27
- package/src/koffi/src/parser.hh +3 -21
- package/src/koffi/src/trampolines/armasm.inc +0 -21
- package/src/koffi/src/trampolines/gnu.inc +0 -21
- package/src/koffi/src/trampolines/masm32.inc +0 -21
- package/src/koffi/src/trampolines/masm64.inc +0 -21
- package/src/koffi/src/trampolines/prototypes.inc +0 -21
- package/src/koffi/src/util.cc +50 -64
- package/src/koffi/src/util.hh +8 -25
- package/src/koffi/src/uv.cc +193 -0
- package/src/koffi/src/uv.def +10 -0
- package/src/koffi/src/uv.hh +40 -0
- package/src/koffi/src/win32.cc +2 -20
- package/src/koffi/src/win32.hh +3 -21
- package/vendor/node-api-headers/CHANGELOG.md +22 -0
- package/vendor/node-api-headers/README.md +6 -17
- package/vendor/node-api-headers/include/js_native_api.h +3 -13
- package/vendor/node-api-headers/include/js_native_api_types.h +15 -0
- package/vendor/node-api-headers/include/node_api.h +0 -4
- package/vendor/node-api-headers/include/node_api_types.h +6 -0
- package/vendor/node-api-headers/include/uv/aix.h +32 -0
- package/vendor/node-api-headers/include/uv/bsd.h +34 -0
- package/vendor/node-api-headers/include/uv/darwin.h +61 -0
- package/vendor/node-api-headers/include/uv/errno.h +483 -0
- package/vendor/node-api-headers/include/uv/linux.h +34 -0
- package/vendor/node-api-headers/include/uv/os390.h +33 -0
- package/vendor/node-api-headers/include/uv/posix.h +31 -0
- package/vendor/node-api-headers/include/uv/sunos.h +44 -0
- package/vendor/node-api-headers/include/uv/threadpool.h +37 -0
- package/vendor/node-api-headers/include/uv/tree.h +521 -0
- package/vendor/node-api-headers/include/uv/unix.h +512 -0
- package/vendor/node-api-headers/include/uv/version.h +43 -0
- package/vendor/node-api-headers/include/uv/win.h +698 -0
- package/vendor/node-api-headers/include/uv.h +1990 -0
- package/vendor/node-api-headers/package.json +1 -1
- package/vendor/node-api-headers/scripts/update-headers.js +6 -0
- package/src/core/base/crc_gen.py +0 -109
|
@@ -1,23 +1,5 @@
|
|
|
1
|
-
//
|
|
2
|
-
|
|
3
|
-
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
4
|
-
// this software and associated documentation files (the “Software”), to deal in
|
|
5
|
-
// the Software without restriction, including without limitation the rights to use,
|
|
6
|
-
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
|
7
|
-
// Software, and to permit persons to whom the Software is furnished to do so,
|
|
8
|
-
// subject to the following conditions:
|
|
9
|
-
|
|
10
|
-
// The above copyright notice and this permission notice shall be included in all
|
|
11
|
-
// copies or substantial portions of the Software.
|
|
12
|
-
|
|
13
|
-
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
|
|
14
|
-
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
15
|
-
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
16
|
-
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
17
|
-
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
18
|
-
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
19
|
-
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
20
|
-
// OTHER DEALINGS IN THE SOFTWARE.
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// SPDX-FileCopyrightText: 2025 Niels Martignène <niels.martignene@protonmail.com>
|
|
21
3
|
|
|
22
4
|
#include "base.hh"
|
|
23
5
|
#include "crc.inc"
|
|
@@ -115,6 +97,7 @@
|
|
|
115
97
|
#if defined(__linux__)
|
|
116
98
|
#include <sys/syscall.h>
|
|
117
99
|
#include <sys/sendfile.h>
|
|
100
|
+
#include <sys/eventfd.h>
|
|
118
101
|
#endif
|
|
119
102
|
#if defined(__APPLE__)
|
|
120
103
|
#include <sys/random.h>
|
|
@@ -767,25 +750,6 @@ int64_t GetUnixTime()
|
|
|
767
750
|
#endif
|
|
768
751
|
}
|
|
769
752
|
|
|
770
|
-
int64_t GetMonotonicTime()
|
|
771
|
-
{
|
|
772
|
-
#if defined(_WIN32)
|
|
773
|
-
return (int64_t)GetTickCount64();
|
|
774
|
-
#elif defined(__EMSCRIPTEN__)
|
|
775
|
-
return (int64_t)emscripten_get_now();
|
|
776
|
-
#elif defined(CLOCK_MONOTONIC_COARSE)
|
|
777
|
-
struct timespec ts;
|
|
778
|
-
K_CRITICAL(clock_gettime(CLOCK_MONOTONIC_COARSE, &ts) == 0, "clock_gettime(CLOCK_MONOTONIC_COARSE) failed: %1", strerror(errno));
|
|
779
|
-
|
|
780
|
-
return (int64_t)ts.tv_sec * 1000 + (int64_t)ts.tv_nsec / 1000000;
|
|
781
|
-
#else
|
|
782
|
-
struct timespec ts;
|
|
783
|
-
K_CRITICAL(clock_gettime(CLOCK_MONOTONIC, &ts) == 0, "clock_gettime(CLOCK_MONOTONIC) failed: %1", strerror(errno));
|
|
784
|
-
|
|
785
|
-
return (int64_t)ts.tv_sec * 1000 + (int64_t)ts.tv_nsec / 1000000;
|
|
786
|
-
#endif
|
|
787
|
-
}
|
|
788
|
-
|
|
789
753
|
TimeSpec DecomposeTimeUTC(int64_t time)
|
|
790
754
|
{
|
|
791
755
|
TimeSpec spec = {};
|
|
@@ -879,6 +843,39 @@ int64_t ComposeTimeUTC(const TimeSpec &spec)
|
|
|
879
843
|
return time;
|
|
880
844
|
}
|
|
881
845
|
|
|
846
|
+
// ------------------------------------------------------------------------
|
|
847
|
+
// Clock
|
|
848
|
+
// ------------------------------------------------------------------------
|
|
849
|
+
|
|
850
|
+
int64_t GetMonotonicClock()
|
|
851
|
+
{
|
|
852
|
+
static std::atomic_int64_t memory;
|
|
853
|
+
|
|
854
|
+
#if defined(_WIN32)
|
|
855
|
+
int64_t clock = (int64_t)GetTickCount64();
|
|
856
|
+
#elif defined(__EMSCRIPTEN__)
|
|
857
|
+
int64_t clock = emscripten_get_now();
|
|
858
|
+
#elif defined(CLOCK_MONOTONIC_COARSE)
|
|
859
|
+
struct timespec ts;
|
|
860
|
+
K_CRITICAL(clock_gettime(CLOCK_MONOTONIC_COARSE, &ts) == 0, "clock_gettime(CLOCK_MONOTONIC_COARSE) failed: %1", strerror(errno));
|
|
861
|
+
|
|
862
|
+
int64_t clock = (int64_t)ts.tv_sec * 1000 + (int64_t)ts.tv_nsec / 1000000;
|
|
863
|
+
#else
|
|
864
|
+
struct timespec ts;
|
|
865
|
+
K_CRITICAL(clock_gettime(CLOCK_MONOTONIC, &ts) == 0, "clock_gettime(CLOCK_MONOTONIC) failed: %1", strerror(errno));
|
|
866
|
+
|
|
867
|
+
int64_t clock = (int64_t)ts.tv_sec * 1000 + (int64_t)ts.tv_nsec / 1000000;
|
|
868
|
+
#endif
|
|
869
|
+
|
|
870
|
+
// Protect against clock going backwards
|
|
871
|
+
int64_t prev = memory.load(std::memory_order_relaxed);
|
|
872
|
+
if (clock < prev) [[unlikely]]
|
|
873
|
+
return prev;
|
|
874
|
+
memory.compare_exchange_weak(prev, clock, std::memory_order_relaxed, std::memory_order_relaxed);
|
|
875
|
+
|
|
876
|
+
return clock;
|
|
877
|
+
}
|
|
878
|
+
|
|
882
879
|
// ------------------------------------------------------------------------
|
|
883
880
|
// Strings
|
|
884
881
|
// ------------------------------------------------------------------------
|
|
@@ -932,6 +929,75 @@ Span<char> DuplicateString(Span<const char> str, Allocator *alloc)
|
|
|
932
929
|
return MakeSpan(new_str, str.len);
|
|
933
930
|
}
|
|
934
931
|
|
|
932
|
+
template <typename CompareFunc>
|
|
933
|
+
static inline int NaturalCmp(Span<const char> str1, Span<const char> str2, CompareFunc cmp)
|
|
934
|
+
{
|
|
935
|
+
Size i = 0;
|
|
936
|
+
Size j = 0;
|
|
937
|
+
|
|
938
|
+
while (i < str1.len && j < str2.len) {
|
|
939
|
+
int delta = cmp(str1[i], str2[j]);
|
|
940
|
+
|
|
941
|
+
if (delta) {
|
|
942
|
+
if (IsAsciiDigit(str1[i]) && IsAsciiDigit(str2[i])) {
|
|
943
|
+
while (i < str1.len && str1[i] == '0') {
|
|
944
|
+
i++;
|
|
945
|
+
}
|
|
946
|
+
while (j < str2.len && str2[j] == '0') {
|
|
947
|
+
j++;
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
bool digit1 = false;
|
|
951
|
+
bool digit2 = false;
|
|
952
|
+
int bias = 0;
|
|
953
|
+
|
|
954
|
+
for (;;) {
|
|
955
|
+
digit1 = (i < str1.len) && IsAsciiDigit(str1[i]);
|
|
956
|
+
digit2 = (j < str2.len) && IsAsciiDigit(str2[j]);
|
|
957
|
+
|
|
958
|
+
if (!digit1 || !digit2)
|
|
959
|
+
break;
|
|
960
|
+
|
|
961
|
+
bias = bias ? bias : cmp(str1[i], str2[j]);
|
|
962
|
+
i++;
|
|
963
|
+
j++;
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
if (!digit1 && !digit2 && bias) {
|
|
967
|
+
return bias;
|
|
968
|
+
} else if (digit1 || digit2) {
|
|
969
|
+
return digit1 ? 1 : -1;
|
|
970
|
+
}
|
|
971
|
+
} else {
|
|
972
|
+
return delta;
|
|
973
|
+
}
|
|
974
|
+
} else {
|
|
975
|
+
i++;
|
|
976
|
+
j++;
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
if (i == str1.len && j < str2.len) {
|
|
981
|
+
return -1;
|
|
982
|
+
} else if (i < str1.len) {
|
|
983
|
+
return 1;
|
|
984
|
+
} else {
|
|
985
|
+
return 0;
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
int CmpNatural(Span<const char> str1, Span<const char> str2)
|
|
990
|
+
{
|
|
991
|
+
auto cmp = [](int a, int b) { return a - b; };
|
|
992
|
+
return NaturalCmp(str1, str2, cmp);
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
int CmpNaturalI(Span<const char> str1, Span<const char> str2)
|
|
996
|
+
{
|
|
997
|
+
auto cmp = [](int a, int b) { return LowerAscii(a) - LowerAscii(b); };
|
|
998
|
+
return NaturalCmp(str1, str2, cmp);
|
|
999
|
+
}
|
|
1000
|
+
|
|
935
1001
|
// ------------------------------------------------------------------------
|
|
936
1002
|
// Format
|
|
937
1003
|
// ------------------------------------------------------------------------
|
|
@@ -940,6 +1006,8 @@ static const char DigitPairs[201] = "0001020304050607080910111213141516171819202
|
|
|
940
1006
|
"25262728293031323334353637383940414243444546474849"
|
|
941
1007
|
"50515253545556575859606162636465666768697071727374"
|
|
942
1008
|
"75767778798081828384858687888990919293949596979899";
|
|
1009
|
+
static const char BigHexLiterals[] = "0123456789ABCDEF";
|
|
1010
|
+
static const char SmallHexLiterals[] = "0123456789abcdef";
|
|
943
1011
|
|
|
944
1012
|
static Span<char> FormatUnsignedToDecimal(uint64_t value, char out_buf[32])
|
|
945
1013
|
{
|
|
@@ -975,13 +1043,11 @@ static Span<char> FormatUnsignedToBinary(uint64_t value, char out_buf[64])
|
|
|
975
1043
|
|
|
976
1044
|
static Span<char> FormatUnsignedToOctal(uint64_t value, char out_buf[64])
|
|
977
1045
|
{
|
|
978
|
-
static const char literals[] = "012345678";
|
|
979
|
-
|
|
980
1046
|
Size offset = 64;
|
|
981
1047
|
do {
|
|
982
1048
|
uint64_t digit = value & 0x7;
|
|
983
1049
|
value >>= 3;
|
|
984
|
-
out_buf[--offset] =
|
|
1050
|
+
out_buf[--offset] = BigHexLiterals[digit];
|
|
985
1051
|
} while (value);
|
|
986
1052
|
|
|
987
1053
|
return MakeSpan(out_buf + offset, 64 - offset);
|
|
@@ -989,13 +1055,11 @@ static Span<char> FormatUnsignedToOctal(uint64_t value, char out_buf[64])
|
|
|
989
1055
|
|
|
990
1056
|
static Span<char> FormatUnsignedToBigHex(uint64_t value, char out_buf[32])
|
|
991
1057
|
{
|
|
992
|
-
static const char literals[] = "0123456789ABCDEF";
|
|
993
|
-
|
|
994
1058
|
Size offset = 32;
|
|
995
1059
|
do {
|
|
996
1060
|
uint64_t digit = value & 0xF;
|
|
997
1061
|
value >>= 4;
|
|
998
|
-
out_buf[--offset] =
|
|
1062
|
+
out_buf[--offset] = BigHexLiterals[digit];
|
|
999
1063
|
} while (value);
|
|
1000
1064
|
|
|
1001
1065
|
return MakeSpan(out_buf + offset, 32 - offset);
|
|
@@ -1003,13 +1067,11 @@ static Span<char> FormatUnsignedToBigHex(uint64_t value, char out_buf[32])
|
|
|
1003
1067
|
|
|
1004
1068
|
static Span<char> FormatUnsignedToSmallHex(uint64_t value, char out_buf[32])
|
|
1005
1069
|
{
|
|
1006
|
-
static const char literals[] = "0123456789abcdef";
|
|
1007
|
-
|
|
1008
1070
|
Size offset = 32;
|
|
1009
1071
|
do {
|
|
1010
1072
|
uint64_t digit = value & 0xF;
|
|
1011
1073
|
value >>= 4;
|
|
1012
|
-
out_buf[--offset] =
|
|
1074
|
+
out_buf[--offset] = SmallHexLiterals[digit];
|
|
1013
1075
|
} while (value);
|
|
1014
1076
|
|
|
1015
1077
|
return MakeSpan(out_buf + offset, 32 - offset);
|
|
@@ -1185,406 +1247,429 @@ Span<const char> FormatFloatingPoint(T value, bool non_zero, int min_prec, int m
|
|
|
1185
1247
|
#endif
|
|
1186
1248
|
}
|
|
1187
1249
|
|
|
1250
|
+
template <typename AppendFunc>
|
|
1251
|
+
static inline void AppendPad(Size pad, char padding, AppendFunc append)
|
|
1252
|
+
{
|
|
1253
|
+
for (Size i = 0; i < pad; i++) {
|
|
1254
|
+
append(padding);
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
template <typename AppendFunc>
|
|
1259
|
+
static inline void AppendSafe(char c, AppendFunc append)
|
|
1260
|
+
{
|
|
1261
|
+
if (IsAsciiControl(c))
|
|
1262
|
+
return;
|
|
1263
|
+
|
|
1264
|
+
append(c);
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1188
1267
|
template <typename AppendFunc>
|
|
1189
1268
|
static inline void ProcessArg(const FmtArg &arg, AppendFunc append)
|
|
1190
1269
|
{
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
char num_buf[128];
|
|
1194
|
-
Span<const char> out = {};
|
|
1270
|
+
switch (arg.type) {
|
|
1271
|
+
case FmtType::Str: { append(arg.u.str); } break;
|
|
1195
1272
|
|
|
1196
|
-
|
|
1273
|
+
case FmtType::PadStr: {
|
|
1274
|
+
append(arg.u.str);
|
|
1275
|
+
AppendPad(arg.pad - arg.u.str.len, arg.padding, append);
|
|
1276
|
+
} break;
|
|
1277
|
+
case FmtType::RepeatStr: {
|
|
1278
|
+
Span<const char> str = arg.u.repeat.str;
|
|
1197
1279
|
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
case FmtType::Char: { out = MakeSpan(&arg.u.ch, 1); } break;
|
|
1280
|
+
for (int i = 0; i < arg.u.repeat.count; i++) {
|
|
1281
|
+
append(str);
|
|
1282
|
+
}
|
|
1283
|
+
} break;
|
|
1203
1284
|
|
|
1204
|
-
|
|
1285
|
+
case FmtType::Char: { append(MakeSpan(&arg.u.ch, 1)); } break;
|
|
1286
|
+
case FmtType::Buffer: {
|
|
1287
|
+
Span<const char> str = arg.u.buf;
|
|
1288
|
+
append(str);
|
|
1289
|
+
} break;
|
|
1290
|
+
case FmtType::Custom: { arg.u.custom.Format(append); } break;
|
|
1205
1291
|
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
} break;
|
|
1292
|
+
case FmtType::Bool: { append(arg.u.b ? "true" : "false"); } break;
|
|
1293
|
+
|
|
1294
|
+
case FmtType::Integer: {
|
|
1295
|
+
if (arg.u.i < 0) {
|
|
1296
|
+
char buf[128];
|
|
1297
|
+
Span<const char> str = FormatUnsignedToDecimal((uint64_t)-arg.u.i, buf);
|
|
1213
1298
|
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
if (arg.pad_len < 0 && arg.pad_char == '0') {
|
|
1299
|
+
if (arg.pad) {
|
|
1300
|
+
if (arg.padding == '0') {
|
|
1217
1301
|
append('-');
|
|
1302
|
+
AppendPad((Size)arg.pad - str.len - 1, arg.padding, append);
|
|
1218
1303
|
} else {
|
|
1219
|
-
|
|
1304
|
+
AppendPad((Size)arg.pad - str.len - 1, arg.padding, append);
|
|
1305
|
+
append('-');
|
|
1220
1306
|
}
|
|
1221
|
-
|
|
1222
|
-
out_buf.Append(FormatUnsignedToDecimal((uint64_t)-arg.u.i, num_buf));
|
|
1223
|
-
out = out_buf;
|
|
1224
1307
|
} else {
|
|
1225
|
-
|
|
1308
|
+
append('-');
|
|
1226
1309
|
}
|
|
1227
|
-
} break;
|
|
1228
|
-
case FmtType::Unsigned: {
|
|
1229
|
-
out = FormatUnsignedToDecimal(arg.u.u, num_buf);
|
|
1230
|
-
} break;
|
|
1231
|
-
case FmtType::Float: {
|
|
1232
|
-
static const uint32_t ExponentMask = 0x7f800000u;
|
|
1233
|
-
static const uint32_t MantissaMask = 0x007fffffu;
|
|
1234
|
-
static const uint32_t SignMask = 0x80000000u;
|
|
1235
1310
|
|
|
1236
|
-
|
|
1237
|
-
|
|
1311
|
+
append(str);
|
|
1312
|
+
} else {
|
|
1313
|
+
char buf[128];
|
|
1314
|
+
Span<const char> str = FormatUnsignedToDecimal((uint64_t)arg.u.i, buf);
|
|
1238
1315
|
|
|
1239
|
-
|
|
1240
|
-
|
|
1316
|
+
AppendPad((Size)arg.pad - str.len, arg.padding, append);
|
|
1317
|
+
append(str);
|
|
1318
|
+
}
|
|
1319
|
+
} break;
|
|
1320
|
+
case FmtType::Unsigned: {
|
|
1321
|
+
char buf[128];
|
|
1322
|
+
Span<const char> str = FormatUnsignedToDecimal(arg.u.u, buf);
|
|
1241
1323
|
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
out = (u.u32 & SignMask) ? "-Inf" : "Inf";
|
|
1246
|
-
}
|
|
1247
|
-
} else {
|
|
1248
|
-
if (u.u32 & SignMask) {
|
|
1249
|
-
if (arg.pad_len < 0 && arg.pad_char == '0') {
|
|
1250
|
-
append('-');
|
|
1251
|
-
} else {
|
|
1252
|
-
out_buf.Append('-');
|
|
1253
|
-
}
|
|
1324
|
+
AppendPad((Size)arg.pad - str.len, arg.padding, append);
|
|
1325
|
+
append(str);
|
|
1326
|
+
} break;
|
|
1254
1327
|
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
}
|
|
1260
|
-
}
|
|
1261
|
-
} break;
|
|
1262
|
-
case FmtType::Double: {
|
|
1263
|
-
static const uint64_t ExponentMask = 0x7FF0000000000000ull;
|
|
1264
|
-
static const uint64_t MantissaMask = 0x000FFFFFFFFFFFFFull;
|
|
1265
|
-
static const uint64_t SignMask = 0x8000000000000000ull;
|
|
1328
|
+
case FmtType::Float: {
|
|
1329
|
+
static const uint32_t ExponentMask = 0x7f800000u;
|
|
1330
|
+
static const uint32_t MantissaMask = 0x007fffffu;
|
|
1331
|
+
static const uint32_t SignMask = 0x80000000u;
|
|
1266
1332
|
|
|
1267
|
-
|
|
1268
|
-
|
|
1333
|
+
union { float f; uint32_t u32; } u;
|
|
1334
|
+
u.f = arg.u.f.value;
|
|
1269
1335
|
|
|
1270
|
-
|
|
1271
|
-
|
|
1336
|
+
if ((u.u32 & ExponentMask) == ExponentMask) {
|
|
1337
|
+
uint32_t mantissa = u.u32 & MantissaMask;
|
|
1272
1338
|
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
} else {
|
|
1276
|
-
out = (u.u64 & SignMask) ? "-Inf" : "Inf";
|
|
1277
|
-
}
|
|
1339
|
+
if (mantissa) {
|
|
1340
|
+
append("NaN");
|
|
1278
1341
|
} else {
|
|
1279
|
-
|
|
1280
|
-
if (arg.pad_len < 0 && arg.pad_char == '0') {
|
|
1281
|
-
append('-');
|
|
1282
|
-
} else {
|
|
1283
|
-
out_buf.Append('-');
|
|
1284
|
-
}
|
|
1285
|
-
|
|
1286
|
-
out_buf.Append(FormatFloatingPoint(-u.d, true, arg.u.d.min_prec, arg.u.d.max_prec, num_buf));
|
|
1287
|
-
out = out_buf;
|
|
1288
|
-
} else {
|
|
1289
|
-
out = FormatFloatingPoint(u.d, u.u64, arg.u.d.min_prec, arg.u.d.max_prec, num_buf);
|
|
1290
|
-
}
|
|
1342
|
+
append((u.u32 & SignMask) ? "-Inf" : "Inf");
|
|
1291
1343
|
}
|
|
1292
|
-
}
|
|
1293
|
-
|
|
1294
|
-
out = FormatUnsignedToBinary(arg.u.u, num_buf);
|
|
1295
|
-
} break;
|
|
1296
|
-
case FmtType::Octal: {
|
|
1297
|
-
out = FormatUnsignedToOctal(arg.u.u, num_buf);
|
|
1298
|
-
} break;
|
|
1299
|
-
case FmtType::BigHex: {
|
|
1300
|
-
out = FormatUnsignedToBigHex(arg.u.u, num_buf);
|
|
1301
|
-
} break;
|
|
1302
|
-
case FmtType::SmallHex: {
|
|
1303
|
-
out = FormatUnsignedToSmallHex(arg.u.u, num_buf);
|
|
1304
|
-
} break;
|
|
1344
|
+
} else {
|
|
1345
|
+
char buf[128];
|
|
1305
1346
|
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
size = (double)-arg.u.i;
|
|
1310
|
-
if (arg.pad_len < 0 && arg.pad_char == '0') {
|
|
1311
|
-
append('-');
|
|
1312
|
-
} else {
|
|
1313
|
-
out_buf.Append('-');
|
|
1314
|
-
}
|
|
1347
|
+
if (u.u32 & SignMask) {
|
|
1348
|
+
append('-');
|
|
1349
|
+
append(FormatFloatingPoint(-u.f, true, arg.u.f.min_prec, arg.u.f.max_prec, buf));
|
|
1315
1350
|
} else {
|
|
1316
|
-
|
|
1351
|
+
append(FormatFloatingPoint(u.f, u.u32, arg.u.f.min_prec, arg.u.f.max_prec, buf));
|
|
1317
1352
|
}
|
|
1353
|
+
}
|
|
1354
|
+
} break;
|
|
1355
|
+
case FmtType::Double: {
|
|
1356
|
+
static const uint64_t ExponentMask = 0x7FF0000000000000ull;
|
|
1357
|
+
static const uint64_t MantissaMask = 0x000FFFFFFFFFFFFFull;
|
|
1358
|
+
static const uint64_t SignMask = 0x8000000000000000ull;
|
|
1318
1359
|
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
int prec = 1 + (size < 9.9995) + (size < 99.995);
|
|
1323
|
-
out_buf.Append(FormatFloatingPoint(size, true, prec, prec, num_buf));
|
|
1324
|
-
out_buf.Append(" GiB");
|
|
1325
|
-
} else if (size >= 1048524.0) {
|
|
1326
|
-
size /= 1048576.0;
|
|
1360
|
+
union { double d; uint64_t u64; } u;
|
|
1361
|
+
u.d = arg.u.d.value;
|
|
1327
1362
|
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
out_buf.Append(" MiB");
|
|
1331
|
-
} else if (size >= 1023.95) {
|
|
1332
|
-
size /= 1024.0;
|
|
1363
|
+
if ((u.u64 & ExponentMask) == ExponentMask) {
|
|
1364
|
+
uint64_t mantissa = u.u64 & MantissaMask;
|
|
1333
1365
|
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
out_buf.Append(" kiB");
|
|
1366
|
+
if (mantissa) {
|
|
1367
|
+
append("NaN");
|
|
1337
1368
|
} else {
|
|
1338
|
-
|
|
1339
|
-
out_buf.Append(" B");
|
|
1369
|
+
append((u.u64 & SignMask) ? "-Inf" : "Inf");
|
|
1340
1370
|
}
|
|
1371
|
+
} else {
|
|
1372
|
+
char buf[128];
|
|
1341
1373
|
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
double size;
|
|
1346
|
-
if (arg.u.i < 0) {
|
|
1347
|
-
size = (double)-arg.u.i;
|
|
1348
|
-
if (arg.pad_len < 0 && arg.pad_char == '0') {
|
|
1349
|
-
append('-');
|
|
1350
|
-
} else {
|
|
1351
|
-
out_buf.Append('-');
|
|
1352
|
-
}
|
|
1374
|
+
if (u.u64 & SignMask) {
|
|
1375
|
+
append('-');
|
|
1376
|
+
append(FormatFloatingPoint(-u.d, true, arg.u.d.min_prec, arg.u.d.max_prec, buf));
|
|
1353
1377
|
} else {
|
|
1354
|
-
|
|
1378
|
+
append(FormatFloatingPoint(u.d, u.u64, arg.u.d.min_prec, arg.u.d.max_prec, buf));
|
|
1355
1379
|
}
|
|
1380
|
+
}
|
|
1381
|
+
} break;
|
|
1356
1382
|
|
|
1357
|
-
|
|
1358
|
-
|
|
1383
|
+
case FmtType::Binary: {
|
|
1384
|
+
char buf[128];
|
|
1385
|
+
Span<const char> str = FormatUnsignedToBinary(arg.u.u, buf);
|
|
1359
1386
|
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1387
|
+
AppendPad((Size)arg.pad - str.len, arg.padding, append);
|
|
1388
|
+
append(str);
|
|
1389
|
+
} break;
|
|
1390
|
+
case FmtType::Octal: {
|
|
1391
|
+
char buf[128];
|
|
1392
|
+
Span<const char> str = FormatUnsignedToOctal(arg.u.u, buf);
|
|
1365
1393
|
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1394
|
+
AppendPad((Size)arg.pad - str.len, arg.padding, append);
|
|
1395
|
+
append(str);
|
|
1396
|
+
} break;
|
|
1397
|
+
case FmtType::BigHex: {
|
|
1398
|
+
char buf[128];
|
|
1399
|
+
Span<const char> str = FormatUnsignedToBigHex(arg.u.u, buf);
|
|
1371
1400
|
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
}
|
|
1401
|
+
AppendPad((Size)arg.pad - str.len, arg.padding, append);
|
|
1402
|
+
append(str);
|
|
1403
|
+
} break;
|
|
1404
|
+
case FmtType::SmallHex: {
|
|
1405
|
+
char buf[128];
|
|
1406
|
+
Span<const char> str = FormatUnsignedToSmallHex(arg.u.u, buf);
|
|
1379
1407
|
|
|
1380
|
-
|
|
1381
|
-
|
|
1408
|
+
AppendPad((Size)arg.pad - str.len, arg.padding, append);
|
|
1409
|
+
append(str);
|
|
1410
|
+
} break;
|
|
1382
1411
|
|
|
1383
|
-
|
|
1384
|
-
|
|
1412
|
+
case FmtType::BigBytes: {
|
|
1413
|
+
for (uint8_t c: arg.u.hex) {
|
|
1414
|
+
char encoded[2];
|
|
1385
1415
|
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
out_buf.Append('-');
|
|
1389
|
-
year = -year;
|
|
1390
|
-
}
|
|
1391
|
-
if (year < 10) {
|
|
1392
|
-
out_buf.Append("000");
|
|
1393
|
-
} else if (year < 100) {
|
|
1394
|
-
out_buf.Append("00");
|
|
1395
|
-
} else if (year < 1000) {
|
|
1396
|
-
out_buf.Append('0');
|
|
1397
|
-
}
|
|
1398
|
-
out_buf.Append(FormatUnsignedToDecimal((uint64_t)year, num_buf));
|
|
1399
|
-
out_buf.Append('-');
|
|
1400
|
-
if (arg.u.date.st.month < 10) {
|
|
1401
|
-
out_buf.Append('0');
|
|
1402
|
-
}
|
|
1403
|
-
out_buf.Append(FormatUnsignedToDecimal((uint64_t)arg.u.date.st.month, num_buf));
|
|
1404
|
-
out_buf.Append('-');
|
|
1405
|
-
if (arg.u.date.st.day < 10) {
|
|
1406
|
-
out_buf.Append('0');
|
|
1407
|
-
}
|
|
1408
|
-
out_buf.Append(FormatUnsignedToDecimal((uint64_t)arg.u.date.st.day, num_buf));
|
|
1409
|
-
out = out_buf;
|
|
1410
|
-
} break;
|
|
1416
|
+
encoded[0] = BigHexLiterals[((uint8_t)c >> 4) & 0xF];
|
|
1417
|
+
encoded[1] = BigHexLiterals[((uint8_t)c >> 0) & 0xF];
|
|
1411
1418
|
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
}
|
|
1446
|
-
|
|
1447
|
-
|
|
1419
|
+
Span<const char> buf = MakeSpan(encoded, 2);
|
|
1420
|
+
append(buf);
|
|
1421
|
+
}
|
|
1422
|
+
} break;
|
|
1423
|
+
case FmtType::SmallBytes: {
|
|
1424
|
+
for (uint8_t c: arg.u.hex) {
|
|
1425
|
+
char encoded[2];
|
|
1426
|
+
|
|
1427
|
+
encoded[0] = SmallHexLiterals[((uint8_t)c >> 4) & 0xF];
|
|
1428
|
+
encoded[1] = SmallHexLiterals[((uint8_t)c >> 0) & 0xF];
|
|
1429
|
+
|
|
1430
|
+
Span<const char> buf = MakeSpan(encoded, 2);
|
|
1431
|
+
append(buf);
|
|
1432
|
+
}
|
|
1433
|
+
} break;
|
|
1434
|
+
|
|
1435
|
+
case FmtType::MemorySize: {
|
|
1436
|
+
char buf[128];
|
|
1437
|
+
|
|
1438
|
+
double size;
|
|
1439
|
+
if (arg.u.i < 0) {
|
|
1440
|
+
append('-');
|
|
1441
|
+
size = (double)-arg.u.i;
|
|
1442
|
+
} else {
|
|
1443
|
+
size = (double)arg.u.i;
|
|
1444
|
+
}
|
|
1445
|
+
|
|
1446
|
+
if (size >= 1073688137.0) {
|
|
1447
|
+
size /= 1073741824.0;
|
|
1448
|
+
|
|
1449
|
+
int prec = 1 + (size < 9.9995) + (size < 99.995);
|
|
1450
|
+
append(FormatFloatingPoint(size, true, prec, prec, buf));
|
|
1451
|
+
append(" GiB");
|
|
1452
|
+
} else if (size >= 1048524.0) {
|
|
1453
|
+
size /= 1048576.0;
|
|
1454
|
+
|
|
1455
|
+
int prec = 1 + (size < 9.9995) + (size < 99.995);
|
|
1456
|
+
append(FormatFloatingPoint(size, true, prec, prec, buf));
|
|
1457
|
+
append(" MiB");
|
|
1458
|
+
} else if (size >= 1023.95) {
|
|
1459
|
+
size /= 1024.0;
|
|
1460
|
+
|
|
1461
|
+
int prec = 1 + (size < 9.9995) + (size < 99.995);
|
|
1462
|
+
append(FormatFloatingPoint(size, true, prec, prec, buf));
|
|
1463
|
+
append(" kiB");
|
|
1464
|
+
} else {
|
|
1465
|
+
append(FormatFloatingPoint(size, arg.u.i, 0, 0, buf));
|
|
1466
|
+
append(" B");
|
|
1467
|
+
}
|
|
1468
|
+
} break;
|
|
1469
|
+
case FmtType::DiskSize: {
|
|
1470
|
+
char buf[128];
|
|
1471
|
+
|
|
1472
|
+
double size;
|
|
1473
|
+
if (arg.u.i < 0) {
|
|
1474
|
+
append('-');
|
|
1475
|
+
size = (double)-arg.u.i;
|
|
1476
|
+
} else {
|
|
1477
|
+
size = (double)arg.u.i;
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
if (size >= 999950000.0) {
|
|
1481
|
+
size /= 1000000000.0;
|
|
1482
|
+
|
|
1483
|
+
int prec = 1 + (size < 9.9995) + (size < 99.995);
|
|
1484
|
+
append(FormatFloatingPoint(size, true, prec, prec, buf));
|
|
1485
|
+
append(" GB");
|
|
1486
|
+
} else if (size >= 999950.0) {
|
|
1487
|
+
size /= 1000000.0;
|
|
1488
|
+
|
|
1489
|
+
int prec = 1 + (size < 9.9995) + (size < 99.995);
|
|
1490
|
+
append(FormatFloatingPoint(size, true, prec, prec, buf));
|
|
1491
|
+
append(" MB");
|
|
1492
|
+
} else if (size >= 999.95) {
|
|
1493
|
+
size /= 1000.0;
|
|
1494
|
+
|
|
1495
|
+
int prec = 1 + (size < 9.9995) + (size < 99.995);
|
|
1496
|
+
append(FormatFloatingPoint(size, true, prec, prec, buf));
|
|
1497
|
+
append(" kB");
|
|
1498
|
+
} else {
|
|
1499
|
+
append(FormatFloatingPoint(size, arg.u.i, 0, 0, buf));
|
|
1500
|
+
append(" B");
|
|
1501
|
+
}
|
|
1502
|
+
} break;
|
|
1503
|
+
|
|
1504
|
+
case FmtType::Date: {
|
|
1505
|
+
K_ASSERT(!arg.u.date.value || arg.u.date.IsValid());
|
|
1448
1506
|
|
|
1507
|
+
char buf[128];
|
|
1508
|
+
|
|
1509
|
+
int year = arg.u.date.st.year;
|
|
1510
|
+
if (year < 0) {
|
|
1511
|
+
append('-');
|
|
1512
|
+
year = -year;
|
|
1513
|
+
}
|
|
1514
|
+
if (year < 10) {
|
|
1515
|
+
append("000");
|
|
1516
|
+
} else if (year < 100) {
|
|
1517
|
+
append("00");
|
|
1518
|
+
} else if (year < 1000) {
|
|
1519
|
+
append('0');
|
|
1520
|
+
}
|
|
1521
|
+
append(FormatUnsignedToDecimal((uint64_t)year, buf));
|
|
1522
|
+
append('-');
|
|
1523
|
+
if (arg.u.date.st.month < 10) {
|
|
1524
|
+
append('0');
|
|
1525
|
+
}
|
|
1526
|
+
append(FormatUnsignedToDecimal((uint64_t)arg.u.date.st.month, buf));
|
|
1527
|
+
append('-');
|
|
1528
|
+
if (arg.u.date.st.day < 10) {
|
|
1529
|
+
append('0');
|
|
1530
|
+
}
|
|
1531
|
+
append(FormatUnsignedToDecimal((uint64_t)arg.u.date.st.day, buf));
|
|
1532
|
+
} break;
|
|
1533
|
+
|
|
1534
|
+
case FmtType::TimeISO: {
|
|
1535
|
+
const TimeSpec &spec = arg.u.time.spec;
|
|
1536
|
+
|
|
1537
|
+
LocalArray<char, 128> buf;
|
|
1538
|
+
|
|
1539
|
+
if (spec.offset && arg.u.time.ms) {
|
|
1449
1540
|
int offset_h = spec.offset / 60;
|
|
1450
1541
|
int offset_m = spec.offset % 60;
|
|
1451
1542
|
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
FmtArg(spec.year).Pad0(-2), FmtArg(spec.month).Pad0(-2),
|
|
1461
|
-
FmtArg(spec.day).Pad0(-2), FmtArg(spec.hour).Pad0(-2),
|
|
1462
|
-
FmtArg(spec.min).Pad0(-2), FmtArg(spec.sec).Pad0(-2),
|
|
1463
|
-
offset_h >= 0 ? "+" : "", FmtArg(offset_h).Pad0(-2), FmtArg(offset_m).Pad0(-2)).len;
|
|
1464
|
-
}
|
|
1465
|
-
out = out_buf;
|
|
1466
|
-
} break;
|
|
1543
|
+
buf.len = Fmt(buf.data, "%1%2%3T%4%5%6.%7%8%9%10",
|
|
1544
|
+
FmtInt(spec.year, 2), FmtInt(spec.month, 2),
|
|
1545
|
+
FmtInt(spec.day, 2), FmtInt(spec.hour, 2),
|
|
1546
|
+
FmtInt(spec.min, 2), FmtInt(spec.sec, 2), FmtInt(spec.msec, 3),
|
|
1547
|
+
offset_h >= 0 ? "+" : "", FmtInt(offset_h, 2), FmtInt(offset_m, 2)).len;
|
|
1548
|
+
} else if (spec.offset) {
|
|
1549
|
+
int offset_h = spec.offset / 60;
|
|
1550
|
+
int offset_m = spec.offset % 60;
|
|
1467
1551
|
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1552
|
+
buf.len = Fmt(buf.data, "%1%2%3T%4%5%6%7%8%9",
|
|
1553
|
+
FmtInt(spec.year, 2), FmtInt(spec.month, 2),
|
|
1554
|
+
FmtInt(spec.day, 2), FmtInt(spec.hour, 2),
|
|
1555
|
+
FmtInt(spec.min, 2), FmtInt(spec.sec, 2),
|
|
1556
|
+
offset_h >= 0 ? "+" : "", FmtInt(offset_h, 2), FmtInt(offset_m, 2)).len;
|
|
1557
|
+
} else if (arg.u.time.ms) {
|
|
1558
|
+
buf.len = Fmt(buf.data, "%1%2%3T%4%5%6.%7Z",
|
|
1559
|
+
FmtInt(spec.year, 2), FmtInt(spec.month, 2),
|
|
1560
|
+
FmtInt(spec.day, 2), FmtInt(spec.hour, 2),
|
|
1561
|
+
FmtInt(spec.min, 2), FmtInt(spec.sec, 2), FmtInt(spec.msec, 3)).len;
|
|
1562
|
+
} else {
|
|
1563
|
+
buf.len = Fmt(buf.data, "%1%2%3T%4%5%6Z",
|
|
1564
|
+
FmtInt(spec.year, 2), FmtInt(spec.month, 2),
|
|
1565
|
+
FmtInt(spec.day, 2), FmtInt(spec.hour, 2),
|
|
1566
|
+
FmtInt(spec.min, 2), FmtInt(spec.sec, 2)).len;
|
|
1567
|
+
}
|
|
1471
1568
|
|
|
1472
|
-
|
|
1569
|
+
append(buf);
|
|
1570
|
+
} break;
|
|
1571
|
+
case FmtType::TimeNice: {
|
|
1572
|
+
const TimeSpec &spec = arg.u.time.spec;
|
|
1473
1573
|
|
|
1474
|
-
|
|
1475
|
-
int rnd = GetRandomInt(0, (int)chars.len);
|
|
1476
|
-
out_buf.Append(chars[rnd]);
|
|
1477
|
-
}
|
|
1574
|
+
LocalArray<char, 128> buf;
|
|
1478
1575
|
|
|
1479
|
-
|
|
1480
|
-
|
|
1576
|
+
if (arg.u.time.ms) {
|
|
1577
|
+
int offset_h = spec.offset / 60;
|
|
1578
|
+
int offset_m = spec.offset % 60;
|
|
1481
1579
|
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1580
|
+
buf.len = Fmt(buf.data, "%1-%2-%3 %4:%5:%6.%7 %8%9%10",
|
|
1581
|
+
FmtInt(spec.year, 2), FmtInt(spec.month, 2),
|
|
1582
|
+
FmtInt(spec.day, 2), FmtInt(spec.hour, 2),
|
|
1583
|
+
FmtInt(spec.min, 2), FmtInt(spec.sec, 2), FmtInt(spec.msec, 3),
|
|
1584
|
+
offset_h >= 0 ? "+" : "", FmtInt(offset_h, 2), FmtInt(offset_m, 2)).len;
|
|
1585
|
+
} else {
|
|
1586
|
+
int offset_h = spec.offset / 60;
|
|
1587
|
+
int offset_m = spec.offset % 60;
|
|
1588
|
+
|
|
1589
|
+
buf.len = Fmt(buf.data, "%1-%2-%3 %4:%5:%6 %7%8%9",
|
|
1590
|
+
FmtInt(spec.year, 2), FmtInt(spec.month, 2),
|
|
1591
|
+
FmtInt(spec.day, 2), FmtInt(spec.hour, 2),
|
|
1592
|
+
FmtInt(spec.min, 2), FmtInt(spec.sec, 2),
|
|
1593
|
+
offset_h >= 0 ? "+" : "", FmtInt(offset_h, 2), FmtInt(offset_m, 2)).len;
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1596
|
+
append(buf);
|
|
1597
|
+
} break;
|
|
1598
|
+
|
|
1599
|
+
case FmtType::List: {
|
|
1600
|
+
Span<const char> separator = arg.u.list.separator;
|
|
1601
|
+
|
|
1602
|
+
if (arg.u.list.u.names.len) {
|
|
1603
|
+
append(arg.u.list.u.names[0]);
|
|
1604
|
+
|
|
1605
|
+
for (Size i = 1; i < arg.u.list.u.names.len; i++) {
|
|
1606
|
+
append(separator);
|
|
1607
|
+
append(arg.u.list.u.names[i]);
|
|
1494
1608
|
}
|
|
1495
|
-
}
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1609
|
+
} else {
|
|
1610
|
+
append(T("None"));
|
|
1611
|
+
}
|
|
1612
|
+
} break;
|
|
1613
|
+
case FmtType::FlagNames: {
|
|
1614
|
+
uint64_t flags = arg.u.list.flags;
|
|
1615
|
+
Span<const char> separator = arg.u.list.separator;
|
|
1616
|
+
|
|
1617
|
+
if (flags) {
|
|
1618
|
+
for (;;) {
|
|
1619
|
+
int idx = CountTrailingZeros(flags);
|
|
1620
|
+
flags &= ~(1ull << idx);
|
|
1621
|
+
|
|
1622
|
+
append(arg.u.list.u.names[idx]);
|
|
1623
|
+
if (!flags)
|
|
1624
|
+
break;
|
|
1625
|
+
append(separator);
|
|
1508
1626
|
}
|
|
1509
|
-
}
|
|
1627
|
+
} else {
|
|
1628
|
+
append(T("None"));
|
|
1629
|
+
}
|
|
1630
|
+
} break;
|
|
1631
|
+
case FmtType::FlagOptions: {
|
|
1632
|
+
uint64_t flags = arg.u.list.flags;
|
|
1633
|
+
Span<const char> separator = arg.u.list.separator;
|
|
1510
1634
|
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
arg2.pad_len = arg.pad_len;
|
|
1516
|
-
arg2.pad_char = arg.pad_char;
|
|
1517
|
-
|
|
1518
|
-
const uint8_t *ptr = (const uint8_t *)arg.u.span.ptr;
|
|
1519
|
-
for (Size j = 0; j < arg.u.span.len; j++) {
|
|
1520
|
-
switch (arg.u.span.type) {
|
|
1521
|
-
case FmtType::Str1: { arg2.u.str1 = *(const char **)ptr; } break;
|
|
1522
|
-
case FmtType::Str2: { arg2.u.str2 = *(const Span<const char> *)ptr; } break;
|
|
1523
|
-
case FmtType::Buffer: { K_UNREACHABLE(); } break;
|
|
1524
|
-
case FmtType::Char: { arg2.u.ch = *(const char *)ptr; } break;
|
|
1525
|
-
case FmtType::Custom: { K_UNREACHABLE(); } break;
|
|
1526
|
-
case FmtType::Bool: { arg2.u.b = *(const bool *)ptr; } break;
|
|
1527
|
-
case FmtType::Integer:
|
|
1528
|
-
case FmtType::Unsigned:
|
|
1529
|
-
case FmtType::Binary:
|
|
1530
|
-
case FmtType::Octal:
|
|
1531
|
-
case FmtType::BigHex:
|
|
1532
|
-
case FmtType::SmallHex: {
|
|
1533
|
-
switch (arg.u.span.type_len) {
|
|
1534
|
-
case 8: { arg2.u.u = *(const uint64_t *)ptr; } break;
|
|
1535
|
-
case 4: { arg2.u.u = *(const uint32_t *)ptr; } break;
|
|
1536
|
-
case 2: { arg2.u.u = *(const uint16_t *)ptr; } break;
|
|
1537
|
-
case 1: { arg2.u.u = *(const uint8_t *)ptr; } break;
|
|
1538
|
-
default: { K_UNREACHABLE(); } break;
|
|
1539
|
-
}
|
|
1540
|
-
} break;
|
|
1541
|
-
case FmtType::Float: {
|
|
1542
|
-
arg2.u.f.value = *(const float *)ptr;
|
|
1543
|
-
arg2.u.d.min_prec = 0;
|
|
1544
|
-
arg2.u.d.max_prec = INT_MAX;
|
|
1545
|
-
} break;
|
|
1546
|
-
case FmtType::Double: {
|
|
1547
|
-
arg2.u.d.value = *(const double *)ptr;
|
|
1548
|
-
arg2.u.d.min_prec = 0;
|
|
1549
|
-
arg2.u.d.max_prec = INT_MAX;
|
|
1550
|
-
} break;
|
|
1551
|
-
case FmtType::MemorySize:
|
|
1552
|
-
case FmtType::DiskSize: { arg2.u.i = *(const int64_t *)ptr; } break;
|
|
1553
|
-
case FmtType::Date: { arg2.u.date = *(const LocalDate *)ptr; } break;
|
|
1554
|
-
case FmtType::TimeISO:
|
|
1555
|
-
case FmtType::TimeNice: { arg2.u.time = *(decltype(FmtArg::u.time) *)ptr; } break;
|
|
1556
|
-
case FmtType::Random: { K_UNREACHABLE(); } break;
|
|
1557
|
-
case FmtType::FlagNames: { K_UNREACHABLE(); } break;
|
|
1558
|
-
case FmtType::FlagOptions: { K_UNREACHABLE(); } break;
|
|
1559
|
-
case FmtType::Span: { K_UNREACHABLE(); } break;
|
|
1560
|
-
}
|
|
1561
|
-
ptr += arg.u.span.type_len;
|
|
1635
|
+
if (arg.u.list.flags) {
|
|
1636
|
+
for (;;) {
|
|
1637
|
+
int idx = CountTrailingZeros(flags);
|
|
1638
|
+
flags &= ~(1ull << idx);
|
|
1562
1639
|
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1640
|
+
append(arg.u.list.u.options[idx].name);
|
|
1641
|
+
if (!flags)
|
|
1642
|
+
break;
|
|
1643
|
+
append(separator);
|
|
1567
1644
|
}
|
|
1645
|
+
} else {
|
|
1646
|
+
append(T("None"));
|
|
1647
|
+
}
|
|
1648
|
+
} break;
|
|
1568
1649
|
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1650
|
+
case FmtType::Random: {
|
|
1651
|
+
LocalArray<char, 512> buf;
|
|
1652
|
+
|
|
1653
|
+
static const char *const DefaultChars = "abcdefghijklmnopqrstuvwxyz0123456789";
|
|
1654
|
+
Span<const char> chars = arg.u.random.chars ? arg.u.random.chars : DefaultChars;
|
|
1572
1655
|
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1656
|
+
K_ASSERT(arg.u.random.len <= K_SIZE(buf.data));
|
|
1657
|
+
buf.len = arg.u.random.len;
|
|
1658
|
+
|
|
1659
|
+
for (Size j = 0; j < arg.u.random.len; j++) {
|
|
1660
|
+
int rnd = GetRandomInt(0, (int)chars.len);
|
|
1661
|
+
buf[j] = chars[rnd];
|
|
1577
1662
|
}
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1663
|
+
|
|
1664
|
+
append(buf);
|
|
1665
|
+
} break;
|
|
1666
|
+
|
|
1667
|
+
case FmtType::SafeStr: {
|
|
1668
|
+
for (char c: arg.u.str) {
|
|
1669
|
+
AppendSafe(c, append);
|
|
1584
1670
|
}
|
|
1585
|
-
}
|
|
1586
|
-
|
|
1587
|
-
}
|
|
1671
|
+
} break;
|
|
1672
|
+
case FmtType::SafeChar: { AppendSafe(arg.u.ch, append); } break;
|
|
1588
1673
|
}
|
|
1589
1674
|
}
|
|
1590
1675
|
|
|
@@ -1864,17 +1949,48 @@ void FmtLowerAscii::Format(FunctionRef<void(Span<const char>)> append) const
|
|
|
1864
1949
|
}
|
|
1865
1950
|
}
|
|
1866
1951
|
|
|
1952
|
+
void FmtUrlSafe::Format(FunctionRef<void(Span<const char>)> append) const
|
|
1953
|
+
{
|
|
1954
|
+
for (char c: str) {
|
|
1955
|
+
if (IsAsciiAlphaOrDigit(c) || strchr(passthrough, c)) {
|
|
1956
|
+
append((char)c);
|
|
1957
|
+
} else {
|
|
1958
|
+
char encoded[3];
|
|
1959
|
+
|
|
1960
|
+
encoded[0] = '%';
|
|
1961
|
+
encoded[1] = BigHexLiterals[((uint8_t)c >> 4) & 0xF];
|
|
1962
|
+
encoded[2] = BigHexLiterals[((uint8_t)c >> 0) & 0xF];
|
|
1963
|
+
|
|
1964
|
+
Span<const char> buf = MakeSpan(encoded, 3);
|
|
1965
|
+
append(buf);
|
|
1966
|
+
}
|
|
1967
|
+
}
|
|
1968
|
+
}
|
|
1969
|
+
|
|
1970
|
+
void FmtHtmlSafe::Format(FunctionRef<void(Span<const char>)> append) const
|
|
1971
|
+
{
|
|
1972
|
+
for (char c: str) {
|
|
1973
|
+
switch (c) {
|
|
1974
|
+
case '<': { append("<"); } break;
|
|
1975
|
+
case '>': { append(">"); } break;
|
|
1976
|
+
case '"': { append("""); } break;
|
|
1977
|
+
case '\'': { append("'"); } break;
|
|
1978
|
+
case '&': { append("&"); } break;
|
|
1979
|
+
|
|
1980
|
+
default: { append(c); } break;
|
|
1981
|
+
}
|
|
1982
|
+
}
|
|
1983
|
+
}
|
|
1984
|
+
|
|
1867
1985
|
void FmtEscape::Format(FunctionRef<void(Span<const char>)> append) const
|
|
1868
1986
|
{
|
|
1869
1987
|
for (char c: str) {
|
|
1870
|
-
if (c == '
|
|
1871
|
-
append("\\\"");
|
|
1872
|
-
} else if (c == '\'') {
|
|
1873
|
-
append("\\'");
|
|
1874
|
-
} else if (c == '\r') {
|
|
1988
|
+
if (c == '\r') {
|
|
1875
1989
|
append("\\r");
|
|
1876
1990
|
} else if (c == '\n') {
|
|
1877
1991
|
append("\\n");
|
|
1992
|
+
} else if (c == '\\') {
|
|
1993
|
+
append("\\\\");
|
|
1878
1994
|
} else if ((unsigned int)c < 32) {
|
|
1879
1995
|
char encoded[4];
|
|
1880
1996
|
|
|
@@ -1885,32 +2001,15 @@ void FmtEscape::Format(FunctionRef<void(Span<const char>)> append) const
|
|
|
1885
2001
|
|
|
1886
2002
|
Span<const char> buf = MakeSpan(encoded, 4);
|
|
1887
2003
|
append(buf);
|
|
2004
|
+
} else if (c == quote) {
|
|
2005
|
+
append('\\');
|
|
2006
|
+
append(quote);
|
|
1888
2007
|
} else {
|
|
1889
2008
|
append(c);
|
|
1890
2009
|
}
|
|
1891
2010
|
}
|
|
1892
2011
|
}
|
|
1893
2012
|
|
|
1894
|
-
void FmtUrlSafe::Format(FunctionRef<void(Span<const char>)> append) const
|
|
1895
|
-
{
|
|
1896
|
-
static const char literals[] = "0123456789ABCDEF";
|
|
1897
|
-
|
|
1898
|
-
for (char c: str) {
|
|
1899
|
-
if (IsAsciiAlphaOrDigit(c) || strchr(passthrough, c)) {
|
|
1900
|
-
append((char)c);
|
|
1901
|
-
} else {
|
|
1902
|
-
char encoded[3];
|
|
1903
|
-
|
|
1904
|
-
encoded[0] = '%';
|
|
1905
|
-
encoded[1] = literals[((uint8_t)c >> 4) & 0xF];
|
|
1906
|
-
encoded[2] = literals[((uint8_t)c >> 0) & 0xF];
|
|
1907
|
-
|
|
1908
|
-
Span<const char> buf = MakeSpan(encoded, 3);
|
|
1909
|
-
append(buf);
|
|
1910
|
-
}
|
|
1911
|
-
}
|
|
1912
|
-
}
|
|
1913
|
-
|
|
1914
2013
|
FmtArg FmtVersion(int64_t version, int parts, int by)
|
|
1915
2014
|
{
|
|
1916
2015
|
K_ASSERT(version >= 0);
|
|
@@ -1946,7 +2045,7 @@ FmtArg FmtVersion(int64_t version, int parts, int by)
|
|
|
1946
2045
|
// Debug and errors
|
|
1947
2046
|
// ------------------------------------------------------------------------
|
|
1948
2047
|
|
|
1949
|
-
static int64_t
|
|
2048
|
+
static int64_t start_clock = GetMonotonicClock();
|
|
1950
2049
|
|
|
1951
2050
|
static std::function<LogFunc> log_handler = DefaultLogHandler;
|
|
1952
2051
|
static bool log_vt100 = FileIsVt100(STDERR_FILENO);
|
|
@@ -1963,7 +2062,7 @@ const char *GetEnv(const char *name)
|
|
|
1963
2062
|
static HashMap<const char *, const char *> values;
|
|
1964
2063
|
|
|
1965
2064
|
bool inserted;
|
|
1966
|
-
auto bucket = values.
|
|
2065
|
+
auto bucket = values.InsertOrGetDefault(name, &inserted);
|
|
1967
2066
|
|
|
1968
2067
|
if (inserted) {
|
|
1969
2068
|
const char *str = (const char *)EM_ASM_INT({
|
|
@@ -2045,8 +2144,8 @@ void LogFmt(LogLevel level, const char *ctx, const char *fmt, Span<const FmtArg>
|
|
|
2045
2144
|
|
|
2046
2145
|
char ctx_buf[512];
|
|
2047
2146
|
if (log_times) {
|
|
2048
|
-
double time = (double)(
|
|
2049
|
-
Fmt(ctx_buf, "[%1] %2", FmtDouble(time, 3
|
|
2147
|
+
double time = (double)(GetMonotonicClock() - start_clock) / 1000;
|
|
2148
|
+
Fmt(ctx_buf, "[%1] %2", FmtDouble(time, 3, 8), ctx ? ctx : "");
|
|
2050
2149
|
|
|
2051
2150
|
ctx = ctx_buf;
|
|
2052
2151
|
}
|
|
@@ -2383,13 +2482,13 @@ void DefaultProgressHandler(Span<const ProgressInfo> bars)
|
|
|
2383
2482
|
int progress = (int)(100 * delta / range);
|
|
2384
2483
|
int size = progress / 4;
|
|
2385
2484
|
|
|
2386
|
-
buf.len += Fmt(buf.TakeAvailable(), true, "%!..+[%1%2]%!0 %3\n",
|
|
2485
|
+
buf.len += Fmt(buf.TakeAvailable(), true, "%!..+[%1%2]%!0 %3\n", FmtRepeat("=", size), FmtRepeat(" ", 25 - size), bar.text).len;
|
|
2387
2486
|
} else {
|
|
2388
2487
|
int progress = (int)(frame % 44);
|
|
2389
2488
|
int before = (progress > 22) ? (44 - progress) : progress;
|
|
2390
2489
|
int after = std::max(22 - before, 0);
|
|
2391
2490
|
|
|
2392
|
-
buf.len += Fmt(buf.TakeAvailable(), true, "%!..+[%1===%2]%!0 %3\n",
|
|
2491
|
+
buf.len += Fmt(buf.TakeAvailable(), true, "%!..+[%1===%2]%!0 %3\n", FmtRepeat(" ", before), FmtRepeat(" ", after), bar.text).len;
|
|
2393
2492
|
}
|
|
2394
2493
|
}
|
|
2395
2494
|
|
|
@@ -2668,7 +2767,7 @@ bool ResizeFile(int fd, const char *filename, int64_t len)
|
|
|
2668
2767
|
return true;
|
|
2669
2768
|
}
|
|
2670
2769
|
|
|
2671
|
-
bool
|
|
2770
|
+
bool SetFileTimes(int fd, const char *filename, int64_t mtime, int64_t btime)
|
|
2672
2771
|
{
|
|
2673
2772
|
HANDLE h = (HANDLE)_get_osfhandle(fd);
|
|
2674
2773
|
|
|
@@ -3117,25 +3216,61 @@ bool ResizeFile(int fd, const char *filename, int64_t len)
|
|
|
3117
3216
|
return true;
|
|
3118
3217
|
}
|
|
3119
3218
|
|
|
3120
|
-
bool
|
|
3219
|
+
bool SetFileMode(int fd, const char *filename, uint32_t mode)
|
|
3220
|
+
{
|
|
3221
|
+
if (fd >= 0) {
|
|
3222
|
+
if (fchmod(fd, (mode_t)mode) < 0) {
|
|
3223
|
+
LogError("Failed to set permissions of '%1': %2", filename, strerror(errno));
|
|
3224
|
+
return false;
|
|
3225
|
+
}
|
|
3226
|
+
} else {
|
|
3227
|
+
if (fchmodat(AT_FDCWD, filename, (mode_t)mode, AT_SYMLINK_NOFOLLOW) < 0) {
|
|
3228
|
+
LogError("Failed to set permissions of '%1': %2", filename, strerror(errno));
|
|
3229
|
+
return false;
|
|
3230
|
+
}
|
|
3231
|
+
}
|
|
3232
|
+
|
|
3233
|
+
return true;
|
|
3234
|
+
}
|
|
3235
|
+
|
|
3236
|
+
bool SetFileOwner(int fd, const char *filename, uint32_t uid, uint32_t gid)
|
|
3237
|
+
{
|
|
3238
|
+
if (fd >= 0) {
|
|
3239
|
+
if (fchown(fd, (uid_t)uid, (gid_t)gid) < 0) {
|
|
3240
|
+
LogError("Failed to change owner of '%1': %2", filename, strerror(errno));
|
|
3241
|
+
return false;
|
|
3242
|
+
}
|
|
3243
|
+
} else {
|
|
3244
|
+
if (lchown(filename, (uid_t)uid, (gid_t)gid) < 0) {
|
|
3245
|
+
LogError("Failed to change owner of '%1': %2", filename, strerror(errno));
|
|
3246
|
+
return false;
|
|
3247
|
+
}
|
|
3248
|
+
}
|
|
3249
|
+
|
|
3250
|
+
return true;
|
|
3251
|
+
}
|
|
3252
|
+
|
|
3253
|
+
bool SetFileTimes(int fd, const char *filename, int64_t mtime, int64_t)
|
|
3121
3254
|
{
|
|
3122
3255
|
struct timespec times[2] = {};
|
|
3123
|
-
bool valid = true;
|
|
3124
3256
|
|
|
3125
3257
|
times[0].tv_nsec = UTIME_OMIT;
|
|
3126
3258
|
times[1].tv_sec = mtime / 1000;
|
|
3127
3259
|
times[1].tv_nsec = (mtime % 1000) * 1000000;
|
|
3128
3260
|
|
|
3129
|
-
if (
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3261
|
+
if (fd >= 0) {
|
|
3262
|
+
if (futimens(fd, times) < 0) {
|
|
3263
|
+
LogError("Failed to set modification time of '%1': %2", filename, strerror(errno));
|
|
3264
|
+
return false;
|
|
3265
|
+
}
|
|
3266
|
+
} else {
|
|
3267
|
+
if (utimensat(AT_FDCWD, filename, times, AT_SYMLINK_NOFOLLOW) < 0) {
|
|
3268
|
+
LogError("Failed to set modification time of '%1': %2", filename, strerror(errno));
|
|
3269
|
+
return false;
|
|
3270
|
+
}
|
|
3136
3271
|
}
|
|
3137
3272
|
|
|
3138
|
-
return
|
|
3273
|
+
return true;
|
|
3139
3274
|
}
|
|
3140
3275
|
|
|
3141
3276
|
#if !defined(__wasm__)
|
|
@@ -3748,7 +3883,7 @@ const char *GetApplicationExecutable()
|
|
|
3748
3883
|
|
|
3749
3884
|
size_t argc;
|
|
3750
3885
|
{
|
|
3751
|
-
int ret = sysctl(name, K_LEN(name), nullptr, &argc,
|
|
3886
|
+
int ret = sysctl(name, K_LEN(name), nullptr, &argc, nullptr, 0);
|
|
3752
3887
|
K_ASSERT(ret >= 0);
|
|
3753
3888
|
K_ASSERT(argc >= 1);
|
|
3754
3889
|
}
|
|
@@ -3783,7 +3918,7 @@ const char *GetApplicationExecutable()
|
|
|
3783
3918
|
int name[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
|
|
3784
3919
|
size_t len = sizeof(executable_path);
|
|
3785
3920
|
|
|
3786
|
-
int ret = sysctl(name, K_LEN(name), executable_path, &len,
|
|
3921
|
+
int ret = sysctl(name, K_LEN(name), executable_path, &len, nullptr, 0);
|
|
3787
3922
|
K_ASSERT(ret >= 0);
|
|
3788
3923
|
K_ASSERT(len < K_SIZE(executable_path));
|
|
3789
3924
|
}
|
|
@@ -3852,13 +3987,6 @@ Span<const char> GetPathExtension(Span<const char> filename, CompressionType *ou
|
|
|
3852
3987
|
return extension;
|
|
3853
3988
|
}
|
|
3854
3989
|
|
|
3855
|
-
CompressionType GetPathCompression(Span<const char> filename)
|
|
3856
|
-
{
|
|
3857
|
-
CompressionType compression_type;
|
|
3858
|
-
GetPathExtension(filename, &compression_type);
|
|
3859
|
-
return compression_type;
|
|
3860
|
-
}
|
|
3861
|
-
|
|
3862
3990
|
Span<char> NormalizePath(Span<const char> path, Span<const char> root_directory, unsigned int flags, Allocator *alloc)
|
|
3863
3991
|
{
|
|
3864
3992
|
K_ASSERT(alloc);
|
|
@@ -3866,6 +3994,21 @@ Span<char> NormalizePath(Span<const char> path, Span<const char> root_directory,
|
|
|
3866
3994
|
if (!path.len && !root_directory.len)
|
|
3867
3995
|
return Fmt(alloc, "");
|
|
3868
3996
|
|
|
3997
|
+
#if !defined(_WIN32)
|
|
3998
|
+
if (!(flags & (int)NormalizeFlag::NoExpansion)) {
|
|
3999
|
+
Span<const char> prefix = SplitStrAny(path, K_PATH_SEPARATORS);
|
|
4000
|
+
|
|
4001
|
+
if (prefix == "~") {
|
|
4002
|
+
const char *home = GetEnv("HOME");
|
|
4003
|
+
|
|
4004
|
+
if (home) {
|
|
4005
|
+
root_directory = home;
|
|
4006
|
+
path = TrimStrLeft(path.Take(1, path.len - 1), K_PATH_SEPARATORS);
|
|
4007
|
+
}
|
|
4008
|
+
}
|
|
4009
|
+
}
|
|
4010
|
+
#endif
|
|
4011
|
+
|
|
3869
4012
|
HeapArray<char> buf(alloc);
|
|
3870
4013
|
Size parts_count = 0;
|
|
3871
4014
|
|
|
@@ -3906,15 +4049,17 @@ Span<char> NormalizePath(Span<const char> path, Span<const char> root_directory,
|
|
|
3906
4049
|
|
|
3907
4050
|
if (!buf.len) {
|
|
3908
4051
|
buf.Append('.');
|
|
4052
|
+
|
|
4053
|
+
if (flags & (int)NormalizeFlag::EndWithSeparator) {
|
|
4054
|
+
buf.Append(separator);
|
|
4055
|
+
}
|
|
3909
4056
|
} else if (buf.len == 1 && IsPathSeparator(buf[0])) {
|
|
3910
|
-
// Root '/', keep as-is
|
|
3911
|
-
|
|
4057
|
+
// Root '/', keep as-is or almost
|
|
4058
|
+
buf[0] = separator;
|
|
4059
|
+
} else if (!(flags & (int)NormalizeFlag::EndWithSeparator)) {
|
|
3912
4060
|
// Strip last separator
|
|
3913
4061
|
buf.len--;
|
|
3914
4062
|
}
|
|
3915
|
-
if (flags & (int)NormalizeFlag::EndWithSeparator) {
|
|
3916
|
-
buf.Append(separator);
|
|
3917
|
-
}
|
|
3918
4063
|
|
|
3919
4064
|
#if defined(_WIN32)
|
|
3920
4065
|
if (buf.len >= 2 && IsAsciiAlpha(buf[0]) && buf[1] == ':') {
|
|
@@ -3961,6 +4106,20 @@ bool PathContainsDotDot(const char *path)
|
|
|
3961
4106
|
return false;
|
|
3962
4107
|
}
|
|
3963
4108
|
|
|
4109
|
+
bool PathContainsDotDot(Span<const char> path)
|
|
4110
|
+
{
|
|
4111
|
+
const char *ptr = path.ptr;
|
|
4112
|
+
const char *end = path.end();
|
|
4113
|
+
|
|
4114
|
+
while ((ptr = (const char *)MemMem(ptr, end - ptr, "..", 2))) {
|
|
4115
|
+
if ((ptr == path.ptr || IsPathSeparator(ptr[-1])) && (ptr + 2 == end || IsPathSeparator(ptr[2])))
|
|
4116
|
+
return true;
|
|
4117
|
+
ptr += 2;
|
|
4118
|
+
}
|
|
4119
|
+
|
|
4120
|
+
return false;
|
|
4121
|
+
}
|
|
4122
|
+
|
|
3964
4123
|
static bool CheckForDumbTerm()
|
|
3965
4124
|
{
|
|
3966
4125
|
static bool dumb = ([]() {
|
|
@@ -4777,6 +4936,7 @@ bool EnsureDirectoryExists(const char *filename)
|
|
|
4777
4936
|
|
|
4778
4937
|
#if defined(_WIN32)
|
|
4779
4938
|
|
|
4939
|
+
static const DWORD main_thread = GetCurrentThreadId();
|
|
4780
4940
|
static HANDLE console_ctrl_event = CreateEvent(nullptr, TRUE, FALSE, nullptr);
|
|
4781
4941
|
static bool ignore_ctrl_event = false;
|
|
4782
4942
|
|
|
@@ -4811,7 +4971,7 @@ bool CreateOverlappedPipe(bool overlap0, bool overlap1, PipeMode mode, HANDLE ou
|
|
|
4811
4971
|
|
|
4812
4972
|
char pipe_name[128];
|
|
4813
4973
|
do {
|
|
4814
|
-
Fmt(pipe_name, "\\\\.\\
|
|
4974
|
+
Fmt(pipe_name, "\\\\.\\pipe\\kcc.%1.%2",
|
|
4815
4975
|
GetCurrentProcessId(), InterlockedIncrement(&pipe_idx));
|
|
4816
4976
|
|
|
4817
4977
|
DWORD open_mode = PIPE_ACCESS_INBOUND | FILE_FLAG_FIRST_PIPE_INSTANCE | (overlap0 ? FILE_FLAG_OVERLAPPED : 0);
|
|
@@ -5138,12 +5298,11 @@ bool ExecuteCommandLine(const char *cmd_line, const ExecuteInfo &info,
|
|
|
5138
5298
|
|
|
5139
5299
|
#else
|
|
5140
5300
|
|
|
5141
|
-
#if defined(__OpenBSD__) || defined(__FreeBSD__)
|
|
5142
5301
|
static const pthread_t main_thread = pthread_self();
|
|
5143
|
-
|
|
5302
|
+
|
|
5144
5303
|
static std::atomic_bool flag_signal { false };
|
|
5145
5304
|
static std::atomic_int explicit_signal { 0 };
|
|
5146
|
-
static
|
|
5305
|
+
static std::atomic_int interrupt_pfd[2] { -1, -1 };
|
|
5147
5306
|
|
|
5148
5307
|
void SetSignalHandler(int signal, void (*func)(int), struct sigaction *prev)
|
|
5149
5308
|
{
|
|
@@ -5158,19 +5317,17 @@ void SetSignalHandler(int signal, void (*func)(int), struct sigaction *prev)
|
|
|
5158
5317
|
|
|
5159
5318
|
static void DefaultSignalHandler(int signal)
|
|
5160
5319
|
{
|
|
5161
|
-
|
|
5162
|
-
if (!pthread_main_np()) {
|
|
5320
|
+
if (pthread_self() != main_thread) {
|
|
5163
5321
|
pthread_kill(main_thread, signal);
|
|
5164
5322
|
return;
|
|
5165
5323
|
}
|
|
5166
|
-
#endif
|
|
5167
5324
|
|
|
5168
5325
|
pid_t pid = getpid();
|
|
5169
5326
|
K_ASSERT(pid > 1);
|
|
5170
5327
|
|
|
5171
|
-
if (interrupt_pfd[1] >= 0) {
|
|
5328
|
+
if (int fd = interrupt_pfd[1].load(); fd >= 0) {
|
|
5172
5329
|
char dummy = 0;
|
|
5173
|
-
K_IGNORE write(
|
|
5330
|
+
K_IGNORE write(fd, &dummy, 1);
|
|
5174
5331
|
}
|
|
5175
5332
|
|
|
5176
5333
|
if (flag_signal) {
|
|
@@ -5181,26 +5338,30 @@ static void DefaultSignalHandler(int signal)
|
|
|
5181
5338
|
}
|
|
5182
5339
|
}
|
|
5183
5340
|
|
|
5184
|
-
bool CreatePipe(int
|
|
5341
|
+
bool CreatePipe(bool block, int out_pfd[2])
|
|
5185
5342
|
{
|
|
5186
5343
|
#if defined(__APPLE__)
|
|
5187
|
-
if (pipe(
|
|
5344
|
+
if (pipe(out_pfd) < 0) {
|
|
5188
5345
|
LogError("Failed to create pipe: %1", strerror(errno));
|
|
5189
5346
|
return false;
|
|
5190
5347
|
}
|
|
5191
5348
|
|
|
5192
|
-
if (fcntl(
|
|
5349
|
+
if (fcntl(out_pfd[0], F_SETFD, FD_CLOEXEC) < 0 || fcntl(out_pfd[1], F_SETFD, FD_CLOEXEC) < 0) {
|
|
5193
5350
|
LogError("Failed to set FD_CLOEXEC on pipe: %1", strerror(errno));
|
|
5194
5351
|
return false;
|
|
5195
5352
|
}
|
|
5196
|
-
if (
|
|
5197
|
-
|
|
5198
|
-
|
|
5353
|
+
if (!block) {
|
|
5354
|
+
if (fcntl(out_pfd[0], F_SETFL, O_NONBLOCK) < 0 || fcntl(out_pfd[1], F_SETFL, O_NONBLOCK) < 0) {
|
|
5355
|
+
LogError("Failed to set O_NONBLOCK on pipe: %1", strerror(errno));
|
|
5356
|
+
return false;
|
|
5357
|
+
}
|
|
5199
5358
|
}
|
|
5200
5359
|
|
|
5201
5360
|
return true;
|
|
5202
5361
|
#else
|
|
5203
|
-
|
|
5362
|
+
int flags = O_CLOEXEC | (block ? 0 : O_NONBLOCK);
|
|
5363
|
+
|
|
5364
|
+
if (pipe2(out_pfd, flags) < 0) {
|
|
5204
5365
|
LogError("Failed to create pipe: %1", strerror(errno));
|
|
5205
5366
|
return false;
|
|
5206
5367
|
}
|
|
@@ -5218,6 +5379,28 @@ void CloseDescriptorSafe(int *fd_ptr)
|
|
|
5218
5379
|
*fd_ptr = -1;
|
|
5219
5380
|
}
|
|
5220
5381
|
|
|
5382
|
+
static void InitInterruptPipe()
|
|
5383
|
+
{
|
|
5384
|
+
static bool success = ([]() {
|
|
5385
|
+
static int pfd[2];
|
|
5386
|
+
|
|
5387
|
+
if (!CreatePipe(false, pfd))
|
|
5388
|
+
return false;
|
|
5389
|
+
|
|
5390
|
+
atexit([]() {
|
|
5391
|
+
CloseDescriptor(pfd[0]);
|
|
5392
|
+
CloseDescriptor(pfd[1]);
|
|
5393
|
+
});
|
|
5394
|
+
|
|
5395
|
+
interrupt_pfd[0] = pfd[0];
|
|
5396
|
+
interrupt_pfd[1] = pfd[1];
|
|
5397
|
+
|
|
5398
|
+
return true;
|
|
5399
|
+
})();
|
|
5400
|
+
|
|
5401
|
+
K_CRITICAL(success, "Failed to create interrupt pipe");
|
|
5402
|
+
}
|
|
5403
|
+
|
|
5221
5404
|
bool ExecuteCommandLine(const char *cmd_line, const ExecuteInfo &info,
|
|
5222
5405
|
FunctionRef<Span<const uint8_t>()> in_func,
|
|
5223
5406
|
FunctionRef<void(Span<uint8_t> buf)> out_func, int *out_code)
|
|
@@ -5231,12 +5414,8 @@ bool ExecuteCommandLine(const char *cmd_line, const ExecuteInfo &info,
|
|
|
5231
5414
|
CloseDescriptorSafe(&in_pfd[1]);
|
|
5232
5415
|
};
|
|
5233
5416
|
if (in_func.IsValid()) {
|
|
5234
|
-
if (!CreatePipe(in_pfd))
|
|
5235
|
-
return false;
|
|
5236
|
-
if (fcntl(in_pfd[1], F_SETFL, O_NONBLOCK) < 0) {
|
|
5237
|
-
LogError("Failed to set O_NONBLOCK on pipe: %1", strerror(errno));
|
|
5417
|
+
if (!CreatePipe(false, in_pfd))
|
|
5238
5418
|
return false;
|
|
5239
|
-
}
|
|
5240
5419
|
}
|
|
5241
5420
|
|
|
5242
5421
|
// Create write pipes
|
|
@@ -5246,33 +5425,11 @@ bool ExecuteCommandLine(const char *cmd_line, const ExecuteInfo &info,
|
|
|
5246
5425
|
CloseDescriptorSafe(&out_pfd[1]);
|
|
5247
5426
|
};
|
|
5248
5427
|
if (out_func.IsValid()) {
|
|
5249
|
-
if (!CreatePipe(out_pfd))
|
|
5250
|
-
return false;
|
|
5251
|
-
if (fcntl(out_pfd[0], F_SETFL, O_NONBLOCK) < 0) {
|
|
5252
|
-
LogError("Failed to set O_NONBLOCK on pipe: %1", strerror(errno));
|
|
5428
|
+
if (!CreatePipe(false, out_pfd))
|
|
5253
5429
|
return false;
|
|
5254
|
-
}
|
|
5255
5430
|
}
|
|
5256
5431
|
|
|
5257
|
-
|
|
5258
|
-
{
|
|
5259
|
-
static bool success = ([]() {
|
|
5260
|
-
if (!CreatePipe(interrupt_pfd))
|
|
5261
|
-
return false;
|
|
5262
|
-
|
|
5263
|
-
atexit([]() {
|
|
5264
|
-
CloseDescriptorSafe(&interrupt_pfd[0]);
|
|
5265
|
-
CloseDescriptorSafe(&interrupt_pfd[1]);
|
|
5266
|
-
});
|
|
5267
|
-
|
|
5268
|
-
return true;
|
|
5269
|
-
})();
|
|
5270
|
-
|
|
5271
|
-
if (!success) {
|
|
5272
|
-
LogError("Failed to create termination pipe");
|
|
5273
|
-
return false;
|
|
5274
|
-
}
|
|
5275
|
-
}
|
|
5432
|
+
InitInterruptPipe();
|
|
5276
5433
|
|
|
5277
5434
|
// Prepare new environment (if needed)
|
|
5278
5435
|
HeapArray<char *> new_env;
|
|
@@ -5345,9 +5502,9 @@ bool ExecuteCommandLine(const char *cmd_line, const ExecuteInfo &info,
|
|
|
5345
5502
|
out_idx = pfds.len;
|
|
5346
5503
|
pfds.Append({ out_pfd[0], POLLIN, 0 });
|
|
5347
5504
|
}
|
|
5348
|
-
if (interrupt_pfd[0] >= 0) {
|
|
5505
|
+
if (int fd = interrupt_pfd[0].load(); fd >= 0) {
|
|
5349
5506
|
term_idx = pfds.len;
|
|
5350
|
-
pfds.Append({
|
|
5507
|
+
pfds.Append({ fd, POLLIN, 0 });
|
|
5351
5508
|
}
|
|
5352
5509
|
|
|
5353
5510
|
if (K_RESTART_EINTR(poll(pfds.data, (nfds_t)pfds.len, -1), < 0) < 0) {
|
|
@@ -5422,7 +5579,7 @@ bool ExecuteCommandLine(const char *cmd_line, const ExecuteInfo &info,
|
|
|
5422
5579
|
// Wait for process exit
|
|
5423
5580
|
int status;
|
|
5424
5581
|
{
|
|
5425
|
-
int64_t start =
|
|
5582
|
+
int64_t start = GetMonotonicClock();
|
|
5426
5583
|
|
|
5427
5584
|
for (;;) {
|
|
5428
5585
|
int ret = K_RESTART_EINTR(waitpid(pid, &status, terminate ? WNOHANG : 0), < 0);
|
|
@@ -5431,7 +5588,7 @@ bool ExecuteCommandLine(const char *cmd_line, const ExecuteInfo &info,
|
|
|
5431
5588
|
LogError("Failed to wait for process exit: %1", strerror(errno));
|
|
5432
5589
|
return false;
|
|
5433
5590
|
} else if (!ret) {
|
|
5434
|
-
int64_t delay =
|
|
5591
|
+
int64_t delay = GetMonotonicClock() - start;
|
|
5435
5592
|
|
|
5436
5593
|
if (delay < 2000) {
|
|
5437
5594
|
// A timeout on waitpid would be better, but... sigh
|
|
@@ -5575,9 +5732,9 @@ WaitResult WaitEvents(Span<const WaitSource> sources, int64_t timeout, uint64_t
|
|
|
5575
5732
|
|
|
5576
5733
|
LocalArray<HANDLE, 64> events;
|
|
5577
5734
|
DWORD wake = 0;
|
|
5735
|
+
DWORD wait_ret = 0;
|
|
5578
5736
|
|
|
5579
5737
|
events.Append(console_ctrl_event);
|
|
5580
|
-
events.Append(wait_msg_event);
|
|
5581
5738
|
|
|
5582
5739
|
// There is no way to get a waitable HANDLE for the Win32 GUI message pump.
|
|
5583
5740
|
// Instead, waitable sources (such as the system tray code) return NULL to indicate that
|
|
@@ -5592,6 +5749,11 @@ WaitResult WaitEvents(Span<const WaitSource> sources, int64_t timeout, uint64_t
|
|
|
5592
5749
|
timeout = (int64_t)std::min((uint64_t)timeout, (uint64_t)src.timeout);
|
|
5593
5750
|
}
|
|
5594
5751
|
|
|
5752
|
+
if (main_thread == GetCurrentThreadId()) {
|
|
5753
|
+
wait_ret = WAIT_OBJECT_0 + (DWORD)events.len;
|
|
5754
|
+
events.Append(wait_msg_event);
|
|
5755
|
+
}
|
|
5756
|
+
|
|
5595
5757
|
DWORD ret;
|
|
5596
5758
|
if (timeout >= 0) {
|
|
5597
5759
|
do {
|
|
@@ -5604,34 +5766,31 @@ WaitResult WaitEvents(Span<const WaitSource> sources, int64_t timeout, uint64_t
|
|
|
5604
5766
|
ret = MsgWaitForMultipleObjects((DWORD)events.len, events.data, FALSE, INFINITE, wake);
|
|
5605
5767
|
}
|
|
5606
5768
|
|
|
5607
|
-
|
|
5608
|
-
|
|
5609
|
-
|
|
5610
|
-
|
|
5611
|
-
|
|
5612
|
-
|
|
5613
|
-
|
|
5614
|
-
|
|
5615
|
-
|
|
5616
|
-
|
|
5617
|
-
|
|
5618
|
-
|
|
5619
|
-
|
|
5620
|
-
}
|
|
5621
|
-
*out_ready = flags;
|
|
5622
|
-
}
|
|
5623
|
-
return WaitResult::Ready;
|
|
5769
|
+
if (ret == WAIT_TIMEOUT) {
|
|
5770
|
+
return WaitResult::Timeout;
|
|
5771
|
+
} else if (ret == WAIT_OBJECT_0) {
|
|
5772
|
+
return WaitResult::Interrupt;
|
|
5773
|
+
} else if (ret == wait_ret) {
|
|
5774
|
+
ResetEvent(wait_msg_event);
|
|
5775
|
+
return WaitResult::Message;
|
|
5776
|
+
} else if (ret == WAIT_OBJECT_0 + events.len) {
|
|
5777
|
+
// Mark all sources with an interest in the message pump as ready
|
|
5778
|
+
if (out_ready) {
|
|
5779
|
+
uint64_t flags = 0;
|
|
5780
|
+
for (Size i = 0; i < sources.len; i++) {
|
|
5781
|
+
flags |= !sources[i].handle ? (1ull << i) : 0;
|
|
5624
5782
|
}
|
|
5783
|
+
*out_ready = flags;
|
|
5784
|
+
}
|
|
5785
|
+
return WaitResult::Ready;
|
|
5786
|
+
} else {
|
|
5787
|
+
Size idx = (Size)ret - WAIT_OBJECT_0 - 1;
|
|
5788
|
+
K_ASSERT(idx >= 0 && idx < sources.len);
|
|
5625
5789
|
|
|
5626
|
-
|
|
5627
|
-
|
|
5628
|
-
|
|
5629
|
-
|
|
5630
|
-
*out_ready |= 1ull << idx;
|
|
5631
|
-
}
|
|
5632
|
-
return WaitResult::Ready;
|
|
5633
|
-
} break;
|
|
5634
|
-
case WAIT_TIMEOUT: return WaitResult::Timeout;
|
|
5790
|
+
if (out_ready) {
|
|
5791
|
+
*out_ready |= 1ull << idx;
|
|
5792
|
+
}
|
|
5793
|
+
return WaitResult::Ready;
|
|
5635
5794
|
}
|
|
5636
5795
|
}
|
|
5637
5796
|
|
|
@@ -5641,11 +5800,16 @@ WaitResult WaitEvents(int64_t timeout)
|
|
|
5641
5800
|
return WaitEvents(sources, timeout);
|
|
5642
5801
|
}
|
|
5643
5802
|
|
|
5644
|
-
void
|
|
5803
|
+
void PostWaitMessage()
|
|
5645
5804
|
{
|
|
5646
5805
|
SetEvent(wait_msg_event);
|
|
5647
5806
|
}
|
|
5648
5807
|
|
|
5808
|
+
void PostTerminate()
|
|
5809
|
+
{
|
|
5810
|
+
SetEvent(console_ctrl_event);
|
|
5811
|
+
}
|
|
5812
|
+
|
|
5649
5813
|
#else
|
|
5650
5814
|
|
|
5651
5815
|
void WaitDelay(int64_t delay)
|
|
@@ -5669,19 +5833,25 @@ void WaitDelay(int64_t delay)
|
|
|
5669
5833
|
WaitResult WaitEvents(Span<const WaitSource> sources, int64_t timeout, uint64_t *out_ready)
|
|
5670
5834
|
{
|
|
5671
5835
|
LocalArray<struct pollfd, 64> pfds;
|
|
5672
|
-
K_ASSERT(sources.len <= K_LEN(pfds.data));
|
|
5673
|
-
|
|
5674
|
-
static std::atomic_bool message { false };
|
|
5836
|
+
K_ASSERT(sources.len <= K_LEN(pfds.data) - 1);
|
|
5675
5837
|
|
|
5838
|
+
// Don't exit after SIGINT/SIGTERM, just signal us
|
|
5676
5839
|
flag_signal = true;
|
|
5840
|
+
|
|
5841
|
+
static std::atomic_bool message { false };
|
|
5677
5842
|
SetSignalHandler(SIGUSR1, [](int) { message = true; });
|
|
5678
5843
|
|
|
5679
5844
|
for (const WaitSource &src: sources) {
|
|
5680
|
-
|
|
5845
|
+
short events = src.events ? (short)src.events : POLLIN;
|
|
5846
|
+
pfds.Append({ src.fd, events, 0 });
|
|
5847
|
+
|
|
5681
5848
|
timeout = (int64_t)std::min((uint64_t)timeout, (uint64_t)src.timeout);
|
|
5682
5849
|
}
|
|
5683
5850
|
|
|
5684
|
-
|
|
5851
|
+
InitInterruptPipe();
|
|
5852
|
+
pfds.Append({ interrupt_pfd[0], POLLIN, 0 });
|
|
5853
|
+
|
|
5854
|
+
int64_t start = (timeout >= 0) ? GetMonotonicClock() : 0;
|
|
5685
5855
|
int64_t until = start + timeout;
|
|
5686
5856
|
int timeout32 = (int)std::min(until - start, (int64_t)INT_MAX);
|
|
5687
5857
|
|
|
@@ -5690,7 +5860,7 @@ WaitResult WaitEvents(Span<const WaitSource> sources, int64_t timeout, uint64_t
|
|
|
5690
5860
|
return WaitResult::Exit;
|
|
5691
5861
|
} else if (explicit_signal) {
|
|
5692
5862
|
return WaitResult::Interrupt;
|
|
5693
|
-
} else if (message) {
|
|
5863
|
+
} else if (message && pthread_self() == main_thread) {
|
|
5694
5864
|
message = false;
|
|
5695
5865
|
return WaitResult::Message;
|
|
5696
5866
|
}
|
|
@@ -5702,23 +5872,26 @@ WaitResult WaitEvents(Span<const WaitSource> sources, int64_t timeout, uint64_t
|
|
|
5702
5872
|
continue;
|
|
5703
5873
|
|
|
5704
5874
|
LogError("Failed to poll for events: %1", strerror(errno));
|
|
5705
|
-
|
|
5875
|
+
abort();
|
|
5706
5876
|
} else if (ready > 0) {
|
|
5707
|
-
|
|
5708
|
-
|
|
5709
|
-
|
|
5710
|
-
|
|
5877
|
+
uint64_t flags = 0;
|
|
5878
|
+
for (Size i = 0; i < pfds.len - 1; i++) {
|
|
5879
|
+
flags |= pfds[i].revents ? (1ull << i) : 0;
|
|
5880
|
+
}
|
|
5881
|
+
|
|
5882
|
+
if (flags) {
|
|
5883
|
+
if (out_ready) {
|
|
5884
|
+
*out_ready = flags;
|
|
5711
5885
|
}
|
|
5712
|
-
|
|
5886
|
+
return WaitResult::Ready;
|
|
5713
5887
|
}
|
|
5714
|
-
return WaitResult::Ready;
|
|
5715
5888
|
}
|
|
5716
5889
|
|
|
5717
5890
|
if (timeout >= 0) {
|
|
5718
|
-
int64_t
|
|
5719
|
-
if (
|
|
5891
|
+
int64_t clock = GetMonotonicClock();
|
|
5892
|
+
if (clock >= until)
|
|
5720
5893
|
break;
|
|
5721
|
-
timeout32 = (int)std::min(until -
|
|
5894
|
+
timeout32 = (int)std::min(until - clock, (int64_t)INT_MAX);
|
|
5722
5895
|
}
|
|
5723
5896
|
}
|
|
5724
5897
|
|
|
@@ -5731,12 +5904,20 @@ WaitResult WaitEvents(int64_t timeout)
|
|
|
5731
5904
|
return WaitEvents(sources, timeout);
|
|
5732
5905
|
}
|
|
5733
5906
|
|
|
5734
|
-
void
|
|
5907
|
+
void PostWaitMessage()
|
|
5735
5908
|
{
|
|
5736
5909
|
pid_t pid = getpid();
|
|
5737
5910
|
kill(pid, SIGUSR1);
|
|
5738
5911
|
}
|
|
5739
5912
|
|
|
5913
|
+
void PostTerminate()
|
|
5914
|
+
{
|
|
5915
|
+
InitInterruptPipe();
|
|
5916
|
+
|
|
5917
|
+
char dummy = 0;
|
|
5918
|
+
K_IGNORE write(interrupt_pfd[1], &dummy, 1);
|
|
5919
|
+
}
|
|
5920
|
+
|
|
5740
5921
|
#endif
|
|
5741
5922
|
|
|
5742
5923
|
#endif
|
|
@@ -5837,30 +6018,30 @@ error:
|
|
|
5837
6018
|
|
|
5838
6019
|
bool NotifySystemd()
|
|
5839
6020
|
{
|
|
5840
|
-
const char *
|
|
5841
|
-
if (!
|
|
6021
|
+
const char *addr = GetEnv("NOTIFY_SOCKET");
|
|
6022
|
+
if (!addr)
|
|
5842
6023
|
return true;
|
|
5843
6024
|
|
|
5844
|
-
struct sockaddr_un
|
|
5845
|
-
if (
|
|
5846
|
-
|
|
6025
|
+
struct sockaddr_un sa;
|
|
6026
|
+
if (addr[0] == '@') {
|
|
6027
|
+
addr++;
|
|
5847
6028
|
|
|
5848
|
-
if (strlen(
|
|
6029
|
+
if (strlen(addr) >= sizeof(sa.sun_path) - 1) {
|
|
5849
6030
|
LogError("Abstract socket address in NOTIFY_SOCKET is too long");
|
|
5850
6031
|
return false;
|
|
5851
6032
|
}
|
|
5852
6033
|
|
|
5853
|
-
|
|
5854
|
-
|
|
5855
|
-
CopyString(
|
|
5856
|
-
} else if (
|
|
5857
|
-
if (strlen(
|
|
6034
|
+
sa.sun_family = AF_UNIX;
|
|
6035
|
+
sa.sun_path[0] = 0;
|
|
6036
|
+
CopyString(addr, MakeSpan(sa.sun_path + 1, K_SIZE(sa.sun_path) - 1));
|
|
6037
|
+
} else if (addr[0] == '/') {
|
|
6038
|
+
if (strlen(addr) >= sizeof(sa.sun_path)) {
|
|
5858
6039
|
LogError("Socket pathname in NOTIFY_SOCKET is too long");
|
|
5859
6040
|
return false;
|
|
5860
6041
|
}
|
|
5861
6042
|
|
|
5862
|
-
|
|
5863
|
-
CopyString(
|
|
6043
|
+
sa.sun_family = AF_UNIX;
|
|
6044
|
+
CopyString(addr, sa.sun_path);
|
|
5864
6045
|
} else {
|
|
5865
6046
|
LogError("Invalid socket address in NOTIFY_SOCKET");
|
|
5866
6047
|
return false;
|
|
@@ -5877,8 +6058,8 @@ bool NotifySystemd()
|
|
|
5877
6058
|
struct msghdr msg = {};
|
|
5878
6059
|
iov.iov_base = (void *)"READY=1";
|
|
5879
6060
|
iov.iov_len = strlen("READY=1");
|
|
5880
|
-
msg.msg_name = &
|
|
5881
|
-
msg.msg_namelen = offsetof(struct sockaddr_un, sun_path) + strlen(
|
|
6061
|
+
msg.msg_name = &sa;
|
|
6062
|
+
msg.msg_namelen = offsetof(struct sockaddr_un, sun_path) + strlen(addr);
|
|
5882
6063
|
msg.msg_iov = &iov;
|
|
5883
6064
|
msg.msg_iovlen = 1;
|
|
5884
6065
|
|
|
@@ -5927,25 +6108,13 @@ void InitApp()
|
|
|
5927
6108
|
#endif
|
|
5928
6109
|
|
|
5929
6110
|
#if !defined(_WIN32) && !defined(__wasi__)
|
|
5930
|
-
// Best effort
|
|
5931
|
-
setpgid(0, 0);
|
|
5932
|
-
|
|
5933
6111
|
// Setup default signal handlers
|
|
5934
6112
|
SetSignalHandler(SIGINT, DefaultSignalHandler);
|
|
5935
6113
|
SetSignalHandler(SIGTERM, DefaultSignalHandler);
|
|
5936
6114
|
SetSignalHandler(SIGHUP, DefaultSignalHandler);
|
|
5937
6115
|
SetSignalHandler(SIGPIPE, [](int) {});
|
|
5938
6116
|
|
|
5939
|
-
|
|
5940
|
-
atexit([]() {
|
|
5941
|
-
if (interrupt_pfd[1] >= 0) {
|
|
5942
|
-
pid_t pid = getpid();
|
|
5943
|
-
K_ASSERT(pid > 1);
|
|
5944
|
-
|
|
5945
|
-
SetSignalHandler(SIGTERM, [](int) {});
|
|
5946
|
-
kill(-pid, SIGTERM);
|
|
5947
|
-
}
|
|
5948
|
-
});
|
|
6117
|
+
InitInterruptPipe();
|
|
5949
6118
|
|
|
5950
6119
|
// Make sure timezone information is in place, which is useful if some kind of sandbox runs later and
|
|
5951
6120
|
// the timezone information is not available (seccomp, namespace, landlock, whatever).
|
|
@@ -6586,7 +6755,7 @@ bool ParseVersion(Span<const char> str, int parts, int multiplier,
|
|
|
6586
6755
|
// ------------------------------------------------------------------------
|
|
6587
6756
|
|
|
6588
6757
|
static thread_local Size rnd_remain;
|
|
6589
|
-
static thread_local int64_t
|
|
6758
|
+
static thread_local int64_t rnd_clock;
|
|
6590
6759
|
#if !defined(_WIN32)
|
|
6591
6760
|
static thread_local pid_t rnd_pid;
|
|
6592
6761
|
#endif
|
|
@@ -6699,7 +6868,7 @@ void FillRandomSafe(void *out_buf, Size len)
|
|
|
6699
6868
|
|
|
6700
6869
|
// Reseed every 4 megabytes, or every hour, or after a fork
|
|
6701
6870
|
reseed |= (rnd_remain <= 0);
|
|
6702
|
-
reseed |= (
|
|
6871
|
+
reseed |= (GetMonotonicClock() - rnd_clock > 3600 * 1000);
|
|
6703
6872
|
#if !defined(_WIN32)
|
|
6704
6873
|
reseed |= (getpid() != rnd_pid);
|
|
6705
6874
|
#endif
|
|
@@ -6727,7 +6896,7 @@ restart:
|
|
|
6727
6896
|
ZeroSafe(&buf, K_SIZE(buf));
|
|
6728
6897
|
|
|
6729
6898
|
rnd_remain = Mebibytes(4);
|
|
6730
|
-
|
|
6899
|
+
rnd_clock = GetMonotonicClock();
|
|
6731
6900
|
#if !defined(_WIN32)
|
|
6732
6901
|
rnd_pid = getpid();
|
|
6733
6902
|
#endif
|
|
@@ -6981,39 +7150,65 @@ int CreateSocket(SocketType type, int flags)
|
|
|
6981
7150
|
|
|
6982
7151
|
#endif
|
|
6983
7152
|
|
|
6984
|
-
bool BindIPSocket(int sock, SocketType type, int port)
|
|
7153
|
+
bool BindIPSocket(int sock, SocketType type, const char *addr, int port)
|
|
6985
7154
|
{
|
|
6986
7155
|
K_ASSERT(type == SocketType::Dual || type == SocketType::IPv4 || type == SocketType::IPv6);
|
|
6987
7156
|
|
|
6988
7157
|
if (type == SocketType::IPv4) {
|
|
6989
|
-
struct sockaddr_in
|
|
7158
|
+
struct sockaddr_in sa = {};
|
|
7159
|
+
|
|
7160
|
+
sa.sin_family = AF_INET;
|
|
7161
|
+
sa.sin_port = htons((uint16_t)port);
|
|
6990
7162
|
|
|
6991
|
-
addr
|
|
6992
|
-
|
|
6993
|
-
|
|
7163
|
+
if (addr) {
|
|
7164
|
+
if (inet_pton(AF_INET, addr, &sa.sin_addr) <= 0) {
|
|
7165
|
+
LogError("Invalid IPv4 address '%1'", addr);
|
|
7166
|
+
return false;
|
|
7167
|
+
}
|
|
7168
|
+
} else {
|
|
7169
|
+
sa.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
7170
|
+
}
|
|
6994
7171
|
|
|
6995
|
-
if (bind(sock, (struct sockaddr *)&
|
|
7172
|
+
if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
|
|
6996
7173
|
#if defined(_WIN32)
|
|
6997
|
-
LogError("Failed to bind to
|
|
7174
|
+
LogError("Failed to bind to '%1:%2': %3", addr ? addr : "*", port, GetWin32ErrorString());
|
|
6998
7175
|
return false;
|
|
6999
7176
|
#else
|
|
7000
|
-
LogError("Failed to bind to
|
|
7177
|
+
LogError("Failed to bind to '%1:%2': %3", addr ? addr : "*", port, strerror(errno));
|
|
7001
7178
|
return false;
|
|
7002
7179
|
#endif
|
|
7003
7180
|
}
|
|
7004
7181
|
} else {
|
|
7005
|
-
struct sockaddr_in6
|
|
7182
|
+
struct sockaddr_in6 sa = {};
|
|
7006
7183
|
|
|
7007
|
-
|
|
7008
|
-
|
|
7009
|
-
|
|
7184
|
+
sa.sin6_family = AF_INET6;
|
|
7185
|
+
sa.sin6_port = htons((uint16_t)port);
|
|
7186
|
+
|
|
7187
|
+
if (addr) {
|
|
7188
|
+
if (!strchr(addr, ':')) {
|
|
7189
|
+
char buf[512];
|
|
7190
|
+
Fmt(buf, "::FFFF:%1", addr);
|
|
7191
|
+
|
|
7192
|
+
if (inet_pton(AF_INET6, buf, &sa.sin6_addr) <= 0) {
|
|
7193
|
+
LogError("Invalid IPv4 or IPv6 address '%1'", addr);
|
|
7194
|
+
return false;
|
|
7195
|
+
}
|
|
7196
|
+
} else {
|
|
7197
|
+
if (inet_pton(AF_INET6, addr, &sa.sin6_addr) <= 0) {
|
|
7198
|
+
LogError("Invalid IPv6 address '%1'", addr);
|
|
7199
|
+
return false;
|
|
7200
|
+
}
|
|
7201
|
+
}
|
|
7202
|
+
} else {
|
|
7203
|
+
sa.sin6_addr = IN6ADDR_ANY_INIT;
|
|
7204
|
+
}
|
|
7010
7205
|
|
|
7011
|
-
if (bind(sock, (struct sockaddr *)&
|
|
7206
|
+
if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
|
|
7012
7207
|
#if defined(_WIN32)
|
|
7013
|
-
LogError("Failed to bind to
|
|
7208
|
+
LogError("Failed to bind to '%1:%2': %3", addr ? addr : "*", port, GetWin32ErrorString());
|
|
7014
7209
|
return false;
|
|
7015
7210
|
#else
|
|
7016
|
-
LogError("Failed to bind to
|
|
7211
|
+
LogError("Failed to bind to '%1:%2': %3", addr ? addr : "*", port, strerror(errno));
|
|
7017
7212
|
return false;
|
|
7018
7213
|
#endif
|
|
7019
7214
|
}
|
|
@@ -7024,16 +7219,32 @@ bool BindIPSocket(int sock, SocketType type, int port)
|
|
|
7024
7219
|
|
|
7025
7220
|
bool BindUnixSocket(int sock, const char *path)
|
|
7026
7221
|
{
|
|
7027
|
-
struct sockaddr_un
|
|
7222
|
+
struct sockaddr_un sa = {};
|
|
7223
|
+
|
|
7224
|
+
// Protect against abtract Unix sockets on Linux
|
|
7225
|
+
if (!path[0]) {
|
|
7226
|
+
LogError("Cannot open empty UNIX socket");
|
|
7227
|
+
return false;
|
|
7228
|
+
}
|
|
7028
7229
|
|
|
7029
|
-
|
|
7030
|
-
if (!CopyString(path,
|
|
7230
|
+
sa.sun_family = AF_UNIX;
|
|
7231
|
+
if (!CopyString(path, sa.sun_path)) {
|
|
7031
7232
|
LogError("Excessive UNIX socket path length");
|
|
7032
7233
|
return false;
|
|
7033
7234
|
}
|
|
7034
7235
|
|
|
7035
|
-
|
|
7036
|
-
|
|
7236
|
+
#if !defined(_WIN32)
|
|
7237
|
+
// Remove existing socket (if any)
|
|
7238
|
+
{
|
|
7239
|
+
struct stat sb;
|
|
7240
|
+
if (!stat(path, &sb) && S_ISSOCK(sb.st_mode)) {
|
|
7241
|
+
LogDebug("Removing existing socket '%1'", path);
|
|
7242
|
+
unlink(path);
|
|
7243
|
+
}
|
|
7244
|
+
}
|
|
7245
|
+
#endif
|
|
7246
|
+
|
|
7247
|
+
if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
|
|
7037
7248
|
#if defined(_WIN32)
|
|
7038
7249
|
LogError("Failed to bind socket to '%1': %2", path, GetWin32ErrorString());
|
|
7039
7250
|
return false;
|
|
@@ -7042,68 +7253,82 @@ bool BindUnixSocket(int sock, const char *path)
|
|
|
7042
7253
|
return false;
|
|
7043
7254
|
#endif
|
|
7044
7255
|
}
|
|
7256
|
+
|
|
7257
|
+
#if !defined(_WIN32)
|
|
7045
7258
|
chmod(path, 0666);
|
|
7259
|
+
#endif
|
|
7046
7260
|
|
|
7047
7261
|
return true;
|
|
7048
7262
|
}
|
|
7049
7263
|
|
|
7050
|
-
|
|
7264
|
+
bool ConnectIPSocket(int sock, const char *addr, int port)
|
|
7051
7265
|
{
|
|
7052
|
-
|
|
7266
|
+
if (strchr(addr, ':')) {
|
|
7267
|
+
struct sockaddr_in6 sa = {};
|
|
7053
7268
|
|
|
7054
|
-
|
|
7055
|
-
|
|
7056
|
-
return -1;
|
|
7057
|
-
K_DEFER_N(err_guard) { CloseSocket(sock); };
|
|
7269
|
+
sa.sin6_family = AF_INET6;
|
|
7270
|
+
sa.sin6_port = htons((unsigned short)port);
|
|
7058
7271
|
|
|
7059
|
-
|
|
7060
|
-
|
|
7272
|
+
if (inet_pton(AF_INET6, addr, &sa.sin6_addr) <= 0) {
|
|
7273
|
+
LogError("Invalid IPv6 address '%1'", addr);
|
|
7274
|
+
return false;
|
|
7275
|
+
}
|
|
7061
7276
|
|
|
7062
|
-
|
|
7063
|
-
|
|
7064
|
-
|
|
7277
|
+
if (connect(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
|
|
7278
|
+
#if defined(_WIN32)
|
|
7279
|
+
LogError("Failed to connect to '%1' (%2): %3", addr, port, GetWin32ErrorString());
|
|
7280
|
+
return false;
|
|
7281
|
+
#else
|
|
7282
|
+
LogError("Failed to connect to '%1' (%2): %3", addr, port, strerror(errno));
|
|
7283
|
+
return false;
|
|
7284
|
+
#endif
|
|
7285
|
+
}
|
|
7286
|
+
} else {
|
|
7287
|
+
struct sockaddr_in sa = {};
|
|
7065
7288
|
|
|
7066
|
-
|
|
7067
|
-
|
|
7068
|
-
int sock = CreateSocket(SocketType::Unix, flags);
|
|
7069
|
-
if (sock < 0)
|
|
7070
|
-
return -1;
|
|
7071
|
-
K_DEFER_N(err_guard) { CloseSocket(sock); };
|
|
7289
|
+
sa.sin_family = AF_INET;
|
|
7290
|
+
sa.sin_port = htons((unsigned short)port);
|
|
7072
7291
|
|
|
7073
|
-
|
|
7074
|
-
|
|
7292
|
+
if (inet_pton(AF_INET, addr, &sa.sin_addr) <= 0) {
|
|
7293
|
+
LogError("Invalid IPv4 address '%1'", addr);
|
|
7294
|
+
return false;
|
|
7295
|
+
}
|
|
7075
7296
|
|
|
7076
|
-
|
|
7077
|
-
|
|
7297
|
+
if (connect(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
|
|
7298
|
+
#if defined(_WIN32)
|
|
7299
|
+
LogError("Failed to connect to '%1' (%2): %3", addr, port, GetWin32ErrorString());
|
|
7300
|
+
return false;
|
|
7301
|
+
#else
|
|
7302
|
+
LogError("Failed to connect to '%1' (%2): %3", addr, port, strerror(errno));
|
|
7303
|
+
return false;
|
|
7304
|
+
#endif
|
|
7305
|
+
}
|
|
7306
|
+
}
|
|
7307
|
+
|
|
7308
|
+
return true;
|
|
7078
7309
|
}
|
|
7079
7310
|
|
|
7080
|
-
int
|
|
7311
|
+
bool ConnectUnixSocket(int sock, const char *path)
|
|
7081
7312
|
{
|
|
7082
|
-
struct sockaddr_un
|
|
7313
|
+
struct sockaddr_un sa = {};
|
|
7083
7314
|
|
|
7084
|
-
|
|
7085
|
-
if (!CopyString(path,
|
|
7315
|
+
sa.sun_family = AF_UNIX;
|
|
7316
|
+
if (!CopyString(path, sa.sun_path)) {
|
|
7086
7317
|
LogError("Excessive UNIX socket path length");
|
|
7087
|
-
return
|
|
7318
|
+
return false;
|
|
7088
7319
|
}
|
|
7089
7320
|
|
|
7090
|
-
|
|
7091
|
-
if (sock < 0)
|
|
7092
|
-
return -1;
|
|
7093
|
-
K_DEFER_N(err_guard) { CloseSocket(sock); };
|
|
7094
|
-
|
|
7095
|
-
if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
|
7321
|
+
if (connect(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
|
|
7096
7322
|
#if defined(_WIN32)
|
|
7097
|
-
LogError("Failed to connect to '%1': %2", path, GetWin32ErrorString());
|
|
7098
|
-
return
|
|
7323
|
+
LogError("Failed to connect to UNIX socket '%1': %2", path, GetWin32ErrorString());
|
|
7324
|
+
return false;
|
|
7099
7325
|
#else
|
|
7100
|
-
LogError("Failed to connect to '%1': %2", path, strerror(errno));
|
|
7101
|
-
return
|
|
7326
|
+
LogError("Failed to connect to UNIX socket '%1': %2", path, strerror(errno));
|
|
7327
|
+
return false;
|
|
7102
7328
|
#endif
|
|
7103
7329
|
}
|
|
7104
7330
|
|
|
7105
|
-
|
|
7106
|
-
return sock;
|
|
7331
|
+
return true;
|
|
7107
7332
|
}
|
|
7108
7333
|
|
|
7109
7334
|
void SetDescriptorNonBlock(int fd, bool enable)
|
|
@@ -7613,14 +7838,12 @@ void StreamReader::SetDecoder(StreamDecoder *decoder)
|
|
|
7613
7838
|
this->decoder = decoder;
|
|
7614
7839
|
}
|
|
7615
7840
|
|
|
7616
|
-
bool StreamReader::Open(Span<const uint8_t> buf, const char *filename,
|
|
7617
|
-
CompressionType compression_type)
|
|
7841
|
+
bool StreamReader::Open(Span<const uint8_t> buf, const char *filename, CompressionType compression_type)
|
|
7618
7842
|
{
|
|
7619
7843
|
Close(true);
|
|
7620
7844
|
|
|
7621
7845
|
K_DEFER_N(err_guard) { error = true; };
|
|
7622
7846
|
|
|
7623
|
-
lazy = flags & (int)StreamReaderFlag::LazyFill;
|
|
7624
7847
|
error = false;
|
|
7625
7848
|
raw_read = 0;
|
|
7626
7849
|
read_total = 0;
|
|
@@ -7640,13 +7863,12 @@ bool StreamReader::Open(Span<const uint8_t> buf, const char *filename, unsigned
|
|
|
7640
7863
|
return true;
|
|
7641
7864
|
}
|
|
7642
7865
|
|
|
7643
|
-
bool StreamReader::Open(int fd, const char *filename,
|
|
7866
|
+
bool StreamReader::Open(int fd, const char *filename, CompressionType compression_type)
|
|
7644
7867
|
{
|
|
7645
7868
|
Close(true);
|
|
7646
7869
|
|
|
7647
7870
|
K_DEFER_N(err_guard) { error = true; };
|
|
7648
7871
|
|
|
7649
|
-
lazy = flags & (int)StreamReaderFlag::LazyFill;
|
|
7650
7872
|
error = false;
|
|
7651
7873
|
raw_read = 0;
|
|
7652
7874
|
read_total = 0;
|
|
@@ -7667,13 +7889,12 @@ bool StreamReader::Open(int fd, const char *filename, unsigned int flags, Compre
|
|
|
7667
7889
|
return true;
|
|
7668
7890
|
}
|
|
7669
7891
|
|
|
7670
|
-
OpenResult StreamReader::Open(const char *filename,
|
|
7892
|
+
OpenResult StreamReader::Open(const char *filename, CompressionType compression_type)
|
|
7671
7893
|
{
|
|
7672
7894
|
Close(true);
|
|
7673
7895
|
|
|
7674
7896
|
K_DEFER_N(err_guard) { error = true; };
|
|
7675
7897
|
|
|
7676
|
-
lazy = flags & (int)StreamReaderFlag::LazyFill;
|
|
7677
7898
|
error = false;
|
|
7678
7899
|
raw_read = 0;
|
|
7679
7900
|
read_total = 0;
|
|
@@ -7697,14 +7918,12 @@ OpenResult StreamReader::Open(const char *filename, unsigned int flags, Compress
|
|
|
7697
7918
|
return OpenResult::Success;
|
|
7698
7919
|
}
|
|
7699
7920
|
|
|
7700
|
-
bool StreamReader::Open(const std::function<Size(Span<uint8_t>)> &func, const char *filename,
|
|
7701
|
-
CompressionType compression_type)
|
|
7921
|
+
bool StreamReader::Open(const std::function<Size(Span<uint8_t>)> &func, const char *filename, CompressionType compression_type)
|
|
7702
7922
|
{
|
|
7703
7923
|
Close(true);
|
|
7704
7924
|
|
|
7705
7925
|
K_DEFER_N(err_guard) { error = true; };
|
|
7706
7926
|
|
|
7707
|
-
lazy = flags & (int)StreamReaderFlag::LazyFill;
|
|
7708
7927
|
error = false;
|
|
7709
7928
|
raw_read = 0;
|
|
7710
7929
|
read_total = 0;
|
|
@@ -7748,7 +7967,6 @@ bool StreamReader::Close(bool implicit)
|
|
|
7748
7967
|
bool ret = !filename || !error;
|
|
7749
7968
|
|
|
7750
7969
|
filename = nullptr;
|
|
7751
|
-
lazy = false;
|
|
7752
7970
|
error = true;
|
|
7753
7971
|
source.type = SourceType::Memory;
|
|
7754
7972
|
source.eof = false;
|
|
@@ -7807,6 +8025,40 @@ void StreamReader::SetDescriptorOwned(bool owned)
|
|
|
7807
8025
|
|
|
7808
8026
|
Size StreamReader::Read(Span<uint8_t> out_buf)
|
|
7809
8027
|
{
|
|
8028
|
+
#if !defined(__wasm__)
|
|
8029
|
+
std::lock_guard<std::mutex> lock(mutex);
|
|
8030
|
+
#endif
|
|
8031
|
+
|
|
8032
|
+
if (error) [[unlikely]]
|
|
8033
|
+
return -1;
|
|
8034
|
+
|
|
8035
|
+
Size len = 0;
|
|
8036
|
+
|
|
8037
|
+
if (decoder) {
|
|
8038
|
+
len = decoder->Read(out_buf.len, out_buf.ptr);
|
|
8039
|
+
if (len < 0) [[unlikely]] {
|
|
8040
|
+
error = true;
|
|
8041
|
+
return -1;
|
|
8042
|
+
}
|
|
8043
|
+
} else {
|
|
8044
|
+
len = ReadRaw(out_buf.len, out_buf.ptr);
|
|
8045
|
+
if (len < 0) [[unlikely]]
|
|
8046
|
+
return -1;
|
|
8047
|
+
eof = source.eof;
|
|
8048
|
+
}
|
|
8049
|
+
|
|
8050
|
+
if (!error && read_max >= 0 && len > read_max - read_total) [[unlikely]] {
|
|
8051
|
+
LogError("Exceeded max stream size of %1", FmtDiskSize(read_max));
|
|
8052
|
+
error = true;
|
|
8053
|
+
return -1;
|
|
8054
|
+
}
|
|
8055
|
+
|
|
8056
|
+
read_total += len;
|
|
8057
|
+
return len;
|
|
8058
|
+
}
|
|
8059
|
+
|
|
8060
|
+
Size StreamReader::ReadFill(Span<uint8_t> out_buf)
|
|
8061
|
+
{
|
|
7810
8062
|
#if !defined(__wasm__)
|
|
7811
8063
|
std::lock_guard<std::mutex> lock(mutex);
|
|
7812
8064
|
#endif
|
|
@@ -7842,7 +8094,7 @@ Size StreamReader::Read(Span<uint8_t> out_buf)
|
|
|
7842
8094
|
return -1;
|
|
7843
8095
|
}
|
|
7844
8096
|
|
|
7845
|
-
if (
|
|
8097
|
+
if (eof)
|
|
7846
8098
|
break;
|
|
7847
8099
|
}
|
|
7848
8100
|
|
|
@@ -7883,10 +8135,10 @@ Size StreamReader::ReadAll(Size max_len, HeapArray<uint8_t> *out_buf)
|
|
|
7883
8135
|
// who need/want to append a NUL character.
|
|
7884
8136
|
out_buf->Grow((Size)raw_len + 1);
|
|
7885
8137
|
|
|
7886
|
-
Size read_len =
|
|
8138
|
+
Size read_len = ReadFill(out_buf->TakeAvailable());
|
|
7887
8139
|
if (read_len < 0)
|
|
7888
8140
|
return -1;
|
|
7889
|
-
out_buf->len += read_len;
|
|
8141
|
+
out_buf->len += (Size)std::min(raw_len, (int64_t)read_len);
|
|
7890
8142
|
|
|
7891
8143
|
buf_guard.Disable();
|
|
7892
8144
|
return read_len;
|
|
@@ -7897,7 +8149,7 @@ Size StreamReader::ReadAll(Size max_len, HeapArray<uint8_t> *out_buf)
|
|
|
7897
8149
|
Size grow = std::min(total_len ? Megabytes(1) : Kibibytes(64), K_SIZE_MAX - out_buf->len);
|
|
7898
8150
|
out_buf->Grow(grow);
|
|
7899
8151
|
|
|
7900
|
-
Size read_len = Read(out_buf->
|
|
8152
|
+
Size read_len = Read(out_buf->TakeAvailable());
|
|
7901
8153
|
if (read_len < 0)
|
|
7902
8154
|
return -1;
|
|
7903
8155
|
|
|
@@ -8032,7 +8284,9 @@ bool LineReader::Next(Span<char> *out_line)
|
|
|
8032
8284
|
if (!view.len) {
|
|
8033
8285
|
buf.Grow(K_LINE_READER_STEP_SIZE + 1);
|
|
8034
8286
|
|
|
8035
|
-
|
|
8287
|
+
Span<char> available = MakeSpan(buf.end(), K_LINE_READER_STEP_SIZE);
|
|
8288
|
+
|
|
8289
|
+
Size read_len = st->Read(available);
|
|
8036
8290
|
if (read_len < 0) {
|
|
8037
8291
|
error = true;
|
|
8038
8292
|
return false;
|
|
@@ -8960,7 +9214,7 @@ bool PatchFile(Span<const uint8_t> data, StreamWriter *writer,
|
|
|
8960
9214
|
bool PatchFile(const AssetInfo &asset, StreamWriter *writer,
|
|
8961
9215
|
FunctionRef<void(Span<const char>, StreamWriter *)> func)
|
|
8962
9216
|
{
|
|
8963
|
-
StreamReader reader(asset.data, "<asset>",
|
|
9217
|
+
StreamReader reader(asset.data, "<asset>", asset.compression_type);
|
|
8964
9218
|
|
|
8965
9219
|
if (!PatchFile(&reader, writer, func)) {
|
|
8966
9220
|
K_ASSERT(reader.IsValid());
|
|
@@ -9025,12 +9279,14 @@ static HeapArray<TranslationTable> i18n_tables;
|
|
|
9025
9279
|
static NoDestroy<HeapArray<TranslationMap>> i18n_maps;
|
|
9026
9280
|
static HashMap<Span<const char> , const TranslationTable *> i18n_locales;
|
|
9027
9281
|
|
|
9028
|
-
static const
|
|
9029
|
-
static
|
|
9282
|
+
static const TranslationTable *i18n_default_table;
|
|
9283
|
+
static const TranslationMap *i18n_default_map;
|
|
9284
|
+
static thread_local const TranslationTable *i18n_thread_table = i18n_default_table;
|
|
9285
|
+
static thread_local const TranslationMap *i18n_thread_map = i18n_default_map;
|
|
9030
9286
|
|
|
9031
|
-
static void SetDefaultLocale()
|
|
9287
|
+
static void SetDefaultLocale(const char *default_lang)
|
|
9032
9288
|
{
|
|
9033
|
-
if (
|
|
9289
|
+
if (i18n_default_table)
|
|
9034
9290
|
return;
|
|
9035
9291
|
|
|
9036
9292
|
// Obey environment settings, even on Windows, for easy override
|
|
@@ -9043,9 +9299,11 @@ static void SetDefaultLocale()
|
|
|
9043
9299
|
|
|
9044
9300
|
if (env) {
|
|
9045
9301
|
ChangeThreadLocale(env);
|
|
9046
|
-
i18n_default = i18n_thread;
|
|
9047
9302
|
|
|
9048
|
-
|
|
9303
|
+
i18n_default_table = i18n_thread_table;
|
|
9304
|
+
i18n_default_map = i18n_thread_map;
|
|
9305
|
+
|
|
9306
|
+
if (i18n_default_table)
|
|
9049
9307
|
return;
|
|
9050
9308
|
}
|
|
9051
9309
|
}
|
|
@@ -9063,9 +9321,11 @@ static void SetDefaultLocale()
|
|
|
9063
9321
|
ConvertWin32WideToUtf8(buffer, lang);
|
|
9064
9322
|
|
|
9065
9323
|
ChangeThreadLocale(lang);
|
|
9066
|
-
i18n_default = i18n_thread;
|
|
9067
9324
|
|
|
9068
|
-
|
|
9325
|
+
i18n_default_table = i18n_thread_table;
|
|
9326
|
+
i18n_default_map = i18n_thread_map;
|
|
9327
|
+
|
|
9328
|
+
if (i18n_default_table)
|
|
9069
9329
|
return;
|
|
9070
9330
|
}
|
|
9071
9331
|
} else {
|
|
@@ -9073,9 +9333,15 @@ static void SetDefaultLocale()
|
|
|
9073
9333
|
}
|
|
9074
9334
|
}
|
|
9075
9335
|
#endif
|
|
9336
|
+
|
|
9337
|
+
ChangeThreadLocale(default_lang);
|
|
9338
|
+
K_CRITICAL(i18n_thread_table, "Missing default locale");
|
|
9339
|
+
|
|
9340
|
+
i18n_default_table = i18n_thread_table;
|
|
9341
|
+
i18n_default_map = i18n_thread_map;
|
|
9076
9342
|
}
|
|
9077
9343
|
|
|
9078
|
-
void InitLocales(Span<const TranslationTable> tables)
|
|
9344
|
+
void InitLocales(Span<const TranslationTable> tables, const char *default_lang)
|
|
9079
9345
|
{
|
|
9080
9346
|
K_ASSERT(!i18n_tables.len);
|
|
9081
9347
|
|
|
@@ -9092,7 +9358,7 @@ void InitLocales(Span<const TranslationTable> tables)
|
|
|
9092
9358
|
i18n_locales.Set(table.language, &table);
|
|
9093
9359
|
}
|
|
9094
9360
|
|
|
9095
|
-
SetDefaultLocale();
|
|
9361
|
+
SetDefaultLocale(default_lang);
|
|
9096
9362
|
}
|
|
9097
9363
|
|
|
9098
9364
|
void ChangeThreadLocale(const char *name)
|
|
@@ -9102,18 +9368,27 @@ void ChangeThreadLocale(const char *name)
|
|
|
9102
9368
|
|
|
9103
9369
|
if (table) {
|
|
9104
9370
|
Size idx = table - i18n_tables.ptr;
|
|
9105
|
-
|
|
9371
|
+
|
|
9372
|
+
i18n_thread_table = table;
|
|
9373
|
+
i18n_thread_map = &(*i18n_maps)[idx];
|
|
9106
9374
|
} else {
|
|
9107
|
-
|
|
9375
|
+
i18n_thread_table = i18n_default_table;
|
|
9376
|
+
i18n_thread_map = i18n_default_map;
|
|
9108
9377
|
}
|
|
9109
9378
|
}
|
|
9110
9379
|
|
|
9380
|
+
const char *GetThreadLocale()
|
|
9381
|
+
{
|
|
9382
|
+
K_ASSERT(i18n_thread_table);
|
|
9383
|
+
return i18n_thread_table->language;
|
|
9384
|
+
}
|
|
9385
|
+
|
|
9111
9386
|
const char *T(const char *key)
|
|
9112
9387
|
{
|
|
9113
|
-
if (!
|
|
9388
|
+
if (!i18n_thread_map)
|
|
9114
9389
|
return key;
|
|
9115
9390
|
|
|
9116
|
-
return
|
|
9391
|
+
return i18n_thread_map->FindValue(key, key);
|
|
9117
9392
|
}
|
|
9118
9393
|
|
|
9119
9394
|
// ------------------------------------------------------------------------
|
|
@@ -9509,6 +9784,10 @@ bool ConsolePrompter::ReadRaw(Span<const char> *out_str)
|
|
|
9509
9784
|
Delete(str_offset, SkipForward(str_offset, 1));
|
|
9510
9785
|
RenderRaw();
|
|
9511
9786
|
}
|
|
9787
|
+
} else if (match_escape("\x1B")) { // Double escape
|
|
9788
|
+
StdErr->Write("\r\n");
|
|
9789
|
+
StdErr->Flush();
|
|
9790
|
+
return false;
|
|
9512
9791
|
} else if (match_escape("\x7F")) { // Alt-Backspace
|
|
9513
9792
|
Delete(FindBackward(str_offset, " \t\r\n"), str_offset);
|
|
9514
9793
|
RenderRaw();
|
|
@@ -9651,11 +9930,54 @@ bool ConsolePrompter::ReadRaw(Span<const char> *out_str)
|
|
|
9651
9930
|
return true;
|
|
9652
9931
|
} break;
|
|
9653
9932
|
|
|
9933
|
+
case '\t': {
|
|
9934
|
+
if (complete) {
|
|
9935
|
+
BlockAllocator temp_alloc;
|
|
9936
|
+
HeapArray<CompleteChoice> choices;
|
|
9937
|
+
|
|
9938
|
+
PushLogFilter([](LogLevel, const char *, const char *, FunctionRef<LogFunc>) {});
|
|
9939
|
+
K_DEFER_N(log_guard) { PopLogFilter(); };
|
|
9940
|
+
|
|
9941
|
+
CompleteResult ret = complete(str, &temp_alloc, &choices);
|
|
9942
|
+
|
|
9943
|
+
switch (ret) {
|
|
9944
|
+
case CompleteResult::Success: {
|
|
9945
|
+
if (choices.len == 1) {
|
|
9946
|
+
const CompleteChoice &choice = choices[0];
|
|
9947
|
+
|
|
9948
|
+
str.RemoveFrom(0);
|
|
9949
|
+
str.Append(choice.value);
|
|
9950
|
+
str_offset = str.len;
|
|
9951
|
+
RenderRaw();
|
|
9952
|
+
} else if (choices.len) {
|
|
9953
|
+
for (const CompleteChoice &choice: choices) {
|
|
9954
|
+
Print(StdErr, "\r\n %!0%!Y..%1%!0", choice.name);
|
|
9955
|
+
}
|
|
9956
|
+
StdErr->Write("\r\n");
|
|
9957
|
+
|
|
9958
|
+
RenderRaw();
|
|
9959
|
+
}
|
|
9960
|
+
} break;
|
|
9961
|
+
|
|
9962
|
+
case CompleteResult::TooMany: {
|
|
9963
|
+
Print(StdErr, "\r\n %!0%!Y..%1%!0\r\n", T("Too many possibilities to show"));
|
|
9964
|
+
RenderRaw();
|
|
9965
|
+
} break;
|
|
9966
|
+
case CompleteResult::Error: {
|
|
9967
|
+
Print(StdErr, "\r\n %!0%!Y..%1%!0\r\n", T("Autocompletion error"));
|
|
9968
|
+
RenderRaw();
|
|
9969
|
+
} break;
|
|
9970
|
+
}
|
|
9971
|
+
|
|
9972
|
+
break;
|
|
9973
|
+
}
|
|
9974
|
+
} [[fallthrough]];
|
|
9975
|
+
|
|
9654
9976
|
default: {
|
|
9655
9977
|
LocalArray<char, 16> frag;
|
|
9656
9978
|
if (uc == '\t') {
|
|
9657
9979
|
frag.Append(" ");
|
|
9658
|
-
} else if (uc
|
|
9980
|
+
} else if (!IsAsciiControl(uc)) {
|
|
9659
9981
|
frag.len = EncodeUtf8(uc, frag.data);
|
|
9660
9982
|
} else {
|
|
9661
9983
|
continue;
|
|
@@ -9731,6 +10053,14 @@ Size ConsolePrompter::ReadRawEnum(Span<const PromptChoice> choices, Size value)
|
|
|
9731
10053
|
fake_input = "\x10";
|
|
9732
10054
|
} else if (match_escape("[B")) { // Down
|
|
9733
10055
|
fake_input = "\x0E";
|
|
10056
|
+
} else if (match_escape("\x1B")) { // Double escape
|
|
10057
|
+
if (rows > y) {
|
|
10058
|
+
Print(StdErr, "\x1B[%1B", rows - y);
|
|
10059
|
+
}
|
|
10060
|
+
StdErr->Write("\r");
|
|
10061
|
+
StdErr->Flush();
|
|
10062
|
+
|
|
10063
|
+
return -1;
|
|
9734
10064
|
}
|
|
9735
10065
|
} break;
|
|
9736
10066
|
|
|
@@ -9792,7 +10122,7 @@ bool ConsolePrompter::ReadBuffered(Span<const char> *out_str)
|
|
|
9792
10122
|
|
|
9793
10123
|
do {
|
|
9794
10124
|
uint8_t c = 0;
|
|
9795
|
-
if (StdIn->Read(
|
|
10125
|
+
if (StdIn->Read(MakeSpan(&c, 1)) < 0)
|
|
9796
10126
|
return false;
|
|
9797
10127
|
|
|
9798
10128
|
if (c == '\n') {
|
|
@@ -9801,7 +10131,7 @@ bool ConsolePrompter::ReadBuffered(Span<const char> *out_str)
|
|
|
9801
10131
|
*out_str = str;
|
|
9802
10132
|
}
|
|
9803
10133
|
return true;
|
|
9804
|
-
} else if (c
|
|
10134
|
+
} else if (!IsAsciiControl(c)) {
|
|
9805
10135
|
str.Append((char)c);
|
|
9806
10136
|
}
|
|
9807
10137
|
} while (!StdIn->IsEOF());
|
|
@@ -9823,7 +10153,7 @@ Size ConsolePrompter::ReadBufferedEnum(Span<const PromptChoice> choices)
|
|
|
9823
10153
|
|
|
9824
10154
|
do {
|
|
9825
10155
|
uint8_t c = 0;
|
|
9826
|
-
if (StdIn->Read(
|
|
10156
|
+
if (StdIn->Read(MakeSpan(&c, 1)) < 0)
|
|
9827
10157
|
return -1;
|
|
9828
10158
|
|
|
9829
10159
|
if (c == '\n') {
|
|
@@ -9840,7 +10170,7 @@ Size ConsolePrompter::ReadBufferedEnum(Span<const PromptChoice> choices)
|
|
|
9840
10170
|
|
|
9841
10171
|
StdErr->Write(prefix);
|
|
9842
10172
|
StdErr->Flush();
|
|
9843
|
-
} else if (c
|
|
10173
|
+
} else if (!IsAsciiControl(c)) {
|
|
9844
10174
|
str.Append((char)c);
|
|
9845
10175
|
}
|
|
9846
10176
|
} while (!StdIn->IsEOF());
|
|
@@ -9944,7 +10274,11 @@ void ConsolePrompter::FormatChoices(Span<const PromptChoice> choices, Size value
|
|
|
9944
10274
|
const PromptChoice &choice = choices[i];
|
|
9945
10275
|
int pad = align - ComputeUnicodeWidth(choice.str);
|
|
9946
10276
|
|
|
9947
|
-
|
|
10277
|
+
if (choice.c) {
|
|
10278
|
+
Fmt(&str, " [%1] %2%3 ", choice.c, choice.str, FmtRepeat(" ", pad));
|
|
10279
|
+
} else {
|
|
10280
|
+
Fmt(&str, " %1%2 ", choice.str, FmtRepeat(" ", pad));
|
|
10281
|
+
}
|
|
9948
10282
|
if (i == value) {
|
|
9949
10283
|
str_offset = str.len;
|
|
9950
10284
|
}
|
|
@@ -9984,7 +10318,7 @@ void ConsolePrompter::RenderRaw()
|
|
|
9984
10318
|
int width = mask ? mask_columns : ComputeUnicodeWidth(str.Take(i, bytes));
|
|
9985
10319
|
|
|
9986
10320
|
if (x2 + width >= columns || str[i] == '\n') {
|
|
9987
|
-
FmtArg prefix =
|
|
10321
|
+
FmtArg prefix = FmtRepeat(" ", prompt_columns - 1);
|
|
9988
10322
|
Print(StdErr, "\x1B[0K\r\n%!D.+%1%!0 %!..+", prefix);
|
|
9989
10323
|
|
|
9990
10324
|
x2 = prompt_columns;
|
|
@@ -10028,7 +10362,7 @@ void ConsolePrompter::RenderBuffered()
|
|
|
10028
10362
|
Print(StdErr, "%1 %2", prompt, line);
|
|
10029
10363
|
while (remain.len) {
|
|
10030
10364
|
line = SplitStr(remain, '\n', &remain);
|
|
10031
|
-
Print(StdErr, "\n%1%2",
|
|
10365
|
+
Print(StdErr, "\n%1%2", FmtRepeat(" ", prompt_columns), line);
|
|
10032
10366
|
}
|
|
10033
10367
|
|
|
10034
10368
|
StdErr->Flush();
|
|
@@ -10206,6 +10540,7 @@ const char *Prompt(const char *prompt, const char *default_value, const char *ma
|
|
|
10206
10540
|
|
|
10207
10541
|
prompter.prompt = prompt;
|
|
10208
10542
|
prompter.mask = mask;
|
|
10543
|
+
|
|
10209
10544
|
prompter.str.allocator = alloc;
|
|
10210
10545
|
if (default_value) {
|
|
10211
10546
|
prompter.str.Append(default_value);
|
|
@@ -10225,11 +10560,12 @@ Size PromptEnum(const char *prompt, Span<const PromptChoice> choices, Size value
|
|
|
10225
10560
|
HashSet<char> keys;
|
|
10226
10561
|
|
|
10227
10562
|
for (const PromptChoice &choice: choices) {
|
|
10228
|
-
|
|
10229
|
-
|
|
10563
|
+
if (!choice.c)
|
|
10564
|
+
continue;
|
|
10230
10565
|
|
|
10231
|
-
|
|
10232
|
-
|
|
10566
|
+
bool duplicates = !keys.InsertOrFail(choice.c);
|
|
10567
|
+
K_ASSERT(!duplicates);
|
|
10568
|
+
}
|
|
10233
10569
|
}
|
|
10234
10570
|
#endif
|
|
10235
10571
|
|
|
@@ -10242,13 +10578,12 @@ Size PromptEnum(const char *prompt, Span<const PromptChoice> choices, Size value
|
|
|
10242
10578
|
Size PromptEnum(const char *prompt, Span<const char *const> strings, Size value)
|
|
10243
10579
|
{
|
|
10244
10580
|
static const char literals[] = "123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
10245
|
-
K_ASSERT(strings.len <= K_LEN(literals));
|
|
10246
10581
|
|
|
10247
10582
|
HeapArray<PromptChoice> choices;
|
|
10248
10583
|
|
|
10249
10584
|
for (Size i = 0; i < strings.len; i++) {
|
|
10250
10585
|
const char *str = strings[i];
|
|
10251
|
-
PromptChoice choice = { literals[i]
|
|
10586
|
+
PromptChoice choice = { str, i < K_LEN(literals) ? literals[i] : (char)0 };
|
|
10252
10587
|
|
|
10253
10588
|
choices.Append(choice);
|
|
10254
10589
|
}
|
|
@@ -10256,6 +10591,134 @@ Size PromptEnum(const char *prompt, Span<const char *const> strings, Size value)
|
|
|
10256
10591
|
return PromptEnum(prompt, choices, value);
|
|
10257
10592
|
}
|
|
10258
10593
|
|
|
10594
|
+
int PromptYN(const char *prompt)
|
|
10595
|
+
{
|
|
10596
|
+
const char *yes = T("Yes");
|
|
10597
|
+
const char *no = T("No");
|
|
10598
|
+
|
|
10599
|
+
const char *shortcuts = T("yn");
|
|
10600
|
+
K_ASSERT(strlen(shortcuts) == 2);
|
|
10601
|
+
|
|
10602
|
+
Size ret = PromptEnum(prompt, {{ yes, shortcuts[0] }, { no, shortcuts[1] }});
|
|
10603
|
+
if (ret < 0)
|
|
10604
|
+
return -1;
|
|
10605
|
+
|
|
10606
|
+
return !ret;
|
|
10607
|
+
}
|
|
10608
|
+
|
|
10609
|
+
const char *PromptPath(const char *prompt, const char *default_path, Span<const char> root_directory, Allocator *alloc)
|
|
10610
|
+
{
|
|
10611
|
+
K_ASSERT(alloc);
|
|
10612
|
+
|
|
10613
|
+
ConsolePrompter prompter;
|
|
10614
|
+
|
|
10615
|
+
prompter.prompt = prompt;
|
|
10616
|
+
prompter.complete = [&](Span<const char> str, Allocator *alloc, HeapArray<CompleteChoice> *out_choices) {
|
|
10617
|
+
Size start_len = out_choices->len;
|
|
10618
|
+
K_DEFER_N(err_guard) { out_choices->RemoveFrom(start_len); };
|
|
10619
|
+
|
|
10620
|
+
Span<const char> path = TrimStrRight(str, K_PATH_SEPARATORS);
|
|
10621
|
+
bool separator = (path.len < str.len);
|
|
10622
|
+
|
|
10623
|
+
// If the value points to a directory, append separator and return
|
|
10624
|
+
if (str.len && !separator) {
|
|
10625
|
+
const char *filename = NormalizePath(path, root_directory, alloc).ptr;
|
|
10626
|
+
|
|
10627
|
+
FileInfo file_info;
|
|
10628
|
+
StatResult ret = StatFile(filename, (int)StatFlag::SilentMissing | (int)StatFlag::FollowSymlink, &file_info);
|
|
10629
|
+
|
|
10630
|
+
if (ret == StatResult::Success && file_info.type == FileType::Directory) {
|
|
10631
|
+
const char *value = Fmt(alloc, "%1%/", path).ptr;
|
|
10632
|
+
out_choices->Append({ value, value });
|
|
10633
|
+
|
|
10634
|
+
err_guard.Disable();
|
|
10635
|
+
return CompleteResult::Success;
|
|
10636
|
+
}
|
|
10637
|
+
}
|
|
10638
|
+
|
|
10639
|
+
Span<const char> directory = path;
|
|
10640
|
+
Span<const char> prefix = separator ? "" : SplitStrReverseAny(path, K_PATH_SEPARATORS, &directory);
|
|
10641
|
+
|
|
10642
|
+
// EnumerateDirectory takes a C string, so we need the NUL terminator,
|
|
10643
|
+
// and we also need to take root_dir into account.
|
|
10644
|
+
const char *dirname = nullptr;
|
|
10645
|
+
|
|
10646
|
+
if (PathIsAbsolute(directory)) {
|
|
10647
|
+
dirname = DuplicateString(directory, alloc).ptr;
|
|
10648
|
+
} else {
|
|
10649
|
+
if (!root_directory.len)
|
|
10650
|
+
return CompleteResult::Success;
|
|
10651
|
+
|
|
10652
|
+
dirname = NormalizePath(directory, root_directory, alloc).ptr;
|
|
10653
|
+
dirname = dirname[0] ? dirname : ".";
|
|
10654
|
+
}
|
|
10655
|
+
|
|
10656
|
+
EnumResult ret = EnumerateDirectory(dirname, nullptr, -1, [&](const char *basename, FileType file_type) {
|
|
10657
|
+
#if defined(_WIN32)
|
|
10658
|
+
if (!StartsWithI(basename, prefix))
|
|
10659
|
+
return true;
|
|
10660
|
+
#else
|
|
10661
|
+
if (!StartsWith(basename, prefix))
|
|
10662
|
+
return true;
|
|
10663
|
+
#endif
|
|
10664
|
+
|
|
10665
|
+
if (out_choices->len - start_len >= K_COMPLETE_PATH_LIMIT)
|
|
10666
|
+
return false;
|
|
10667
|
+
|
|
10668
|
+
CompleteChoice choice;
|
|
10669
|
+
{
|
|
10670
|
+
HeapArray<char> buf(alloc);
|
|
10671
|
+
|
|
10672
|
+
// Make directory part
|
|
10673
|
+
buf.Append(directory);
|
|
10674
|
+
if (directory.len && !IsPathSeparator(directory[directory.len - 1])) {
|
|
10675
|
+
buf.Append(*K_PATH_SEPARATORS);
|
|
10676
|
+
}
|
|
10677
|
+
|
|
10678
|
+
Size name_offset = buf.len;
|
|
10679
|
+
|
|
10680
|
+
// Append name
|
|
10681
|
+
buf.Append(basename);
|
|
10682
|
+
if (file_type == FileType::Directory) {
|
|
10683
|
+
buf.Append(*K_PATH_SEPARATORS);
|
|
10684
|
+
}
|
|
10685
|
+
buf.Append(0);
|
|
10686
|
+
buf.Trim();
|
|
10687
|
+
|
|
10688
|
+
choice.value = buf.Leak().ptr;
|
|
10689
|
+
choice.name = choice.value + name_offset;
|
|
10690
|
+
}
|
|
10691
|
+
|
|
10692
|
+
out_choices->Append(choice);
|
|
10693
|
+
return true;
|
|
10694
|
+
});
|
|
10695
|
+
|
|
10696
|
+
if (ret == EnumResult::CallbackFail) {
|
|
10697
|
+
return CompleteResult::TooMany;
|
|
10698
|
+
} else if (ret != EnumResult::Success) {
|
|
10699
|
+
// Just ignore it and don't print anything
|
|
10700
|
+
return CompleteResult::Success;
|
|
10701
|
+
}
|
|
10702
|
+
|
|
10703
|
+
std::sort(out_choices->ptr + start_len, out_choices->end(),
|
|
10704
|
+
[](const CompleteChoice &choice1, const CompleteChoice &choice2) { return CmpNaturalI(choice1.name, choice2.name) < 0; });
|
|
10705
|
+
|
|
10706
|
+
err_guard.Disable();
|
|
10707
|
+
return CompleteResult::Success;
|
|
10708
|
+
};
|
|
10709
|
+
|
|
10710
|
+
prompter.str.allocator = alloc;
|
|
10711
|
+
if (default_path) {
|
|
10712
|
+
prompter.str.Append(default_path);
|
|
10713
|
+
}
|
|
10714
|
+
|
|
10715
|
+
if (!prompter.Read())
|
|
10716
|
+
return nullptr;
|
|
10717
|
+
|
|
10718
|
+
const char *str = NormalizePath(prompter.str, alloc).ptr;
|
|
10719
|
+
return str;
|
|
10720
|
+
}
|
|
10721
|
+
|
|
10259
10722
|
// ------------------------------------------------------------------------
|
|
10260
10723
|
// Mime types
|
|
10261
10724
|
// ------------------------------------------------------------------------
|
|
@@ -10373,7 +10836,7 @@ static inline int ComputeCharacterWidth(int32_t uc)
|
|
|
10373
10836
|
{
|
|
10374
10837
|
// Fast path
|
|
10375
10838
|
if (uc < 128)
|
|
10376
|
-
return (uc
|
|
10839
|
+
return IsAsciiControl(uc) ? 0 : 1;
|
|
10377
10840
|
|
|
10378
10841
|
if (TestUnicodeTable(WcWidthNull, uc))
|
|
10379
10842
|
return 0;
|