Compare commits
5 Commits
b22bea84ec
...
9bb1720cca
Author | SHA1 | Date | |
---|---|---|---|
9bb1720cca | |||
fe348d56c0 | |||
3a28771520 | |||
3ef484b3f3 | |||
3a70accdeb |
@ -8,6 +8,7 @@ endfunction()
|
|||||||
|
|
||||||
luna_app(init.cpp init)
|
luna_app(init.cpp init)
|
||||||
luna_app(sh.cpp sh)
|
luna_app(sh.cpp sh)
|
||||||
|
luna_app(env.cpp env)
|
||||||
|
|
||||||
luna_app(cat.cpp cat)
|
luna_app(cat.cpp cat)
|
||||||
target_link_libraries(cat PRIVATE os)
|
target_link_libraries(cat PRIVATE os)
|
||||||
|
9
apps/env.cpp
Normal file
9
apps/env.cpp
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
extern char** environ;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
char** env = environ;
|
||||||
|
while (*env) { puts(*(env++)); }
|
||||||
|
}
|
@ -43,7 +43,8 @@ int main()
|
|||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
{
|
{
|
||||||
char* argv[] = { "/bin/sh", NULL };
|
char* argv[] = { "/bin/sh", NULL };
|
||||||
execv(argv[0], argv);
|
char* envp[] = { "PATH=/bin:/sbin", NULL };
|
||||||
|
execve(argv[0], argv, envp);
|
||||||
perror("execv");
|
perror("execv");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
46
apps/sh.cpp
46
apps/sh.cpp
@ -31,50 +31,6 @@ static Result<Vector<char*>> split_command_into_argv(const char* cmd)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Result<String> join_path(const char* str1, const char* str2)
|
|
||||||
{
|
|
||||||
StringBuilder sb;
|
|
||||||
TRY(sb.add(StringView { str1 }));
|
|
||||||
TRY(sb.add(StringView { str2 }));
|
|
||||||
return sb.string();
|
|
||||||
}
|
|
||||||
|
|
||||||
static int execute_in_path(const char* dir, char** argv)
|
|
||||||
{
|
|
||||||
String path;
|
|
||||||
bool ok = join_path(dir, argv[0]).try_move_value_or_error(path, errno);
|
|
||||||
if (!ok) return -1;
|
|
||||||
|
|
||||||
int err = errno;
|
|
||||||
|
|
||||||
int status = execv(path.chars(), argv);
|
|
||||||
|
|
||||||
if (errno == ENOENT)
|
|
||||||
{
|
|
||||||
errno = err;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int sh_execvp(char** argv)
|
|
||||||
{
|
|
||||||
if (strchr(argv[0], '/'))
|
|
||||||
{
|
|
||||||
// Relative paths do not need path resolution.
|
|
||||||
return execv(argv[0], argv);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (execute_in_path("/bin/", argv) != 0) return -1;
|
|
||||||
if (execute_in_path("/sbin/", argv) != 0) return -1;
|
|
||||||
if (execute_in_path("/usr/bin/", argv) != 0) return -1;
|
|
||||||
if (execute_in_path("/usr/local/bin/", argv) != 0) return -1;
|
|
||||||
|
|
||||||
errno = ENOENT;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
while (1)
|
while (1)
|
||||||
@ -105,7 +61,7 @@ int main()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (argv[0] == NULL) return 0;
|
if (argv[0] == NULL) return 0;
|
||||||
sh_execvp(argv.data());
|
execvp(argv[0], argv.data());
|
||||||
perror(argv[0]);
|
perror(argv[0]);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -31,27 +31,13 @@ static Result<Vector<String>> copy_string_vector_from_userspace(u64 address)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Result<u64> copy_string_vector_to_userspace(const Vector<String>& vec, ThreadImage& image)
|
Result<u64> sys_execve(Registers* regs, SyscallArgs args)
|
||||||
{
|
|
||||||
Vector<u64> user_vec;
|
|
||||||
for (const auto& item : vec)
|
|
||||||
{
|
|
||||||
// Copy each individual string and retrieve a userspace pointer to said copy
|
|
||||||
u64 addr = TRY(image.push_mem_on_stack((const u8*)item.chars(), item.length() + 1));
|
|
||||||
TRY(user_vec.try_append(addr));
|
|
||||||
}
|
|
||||||
|
|
||||||
TRY(user_vec.try_append((u64) nullptr));
|
|
||||||
// Copy the actual vector of userspace pointers to the stack
|
|
||||||
return TRY(image.push_mem_on_stack((u8*)user_vec.data(), user_vec.size() * sizeof(u64)));
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<u64> sys_exec(Registers* regs, SyscallArgs args)
|
|
||||||
{
|
{
|
||||||
auto path = TRY(MemoryManager::strdup_from_user(args[0]));
|
auto path = TRY(MemoryManager::strdup_from_user(args[0]));
|
||||||
auto argv = TRY(copy_string_vector_from_userspace(args[1]));
|
auto argv = TRY(copy_string_vector_from_userspace(args[1]));
|
||||||
|
auto envp = TRY(copy_string_vector_from_userspace(args[2]));
|
||||||
|
|
||||||
// FIXME: Make sure argv is not too big.
|
// FIXME: Make sure argv & envp are not too big.
|
||||||
|
|
||||||
auto inode = TRY(VFS::resolve_path(path.chars()));
|
auto inode = TRY(VFS::resolve_path(path.chars()));
|
||||||
|
|
||||||
@ -68,9 +54,14 @@ Result<u64> sys_exec(Registers* regs, SyscallArgs args)
|
|||||||
|
|
||||||
kdbgln("exec: copying argv to image memory (argc = %zu)", argv.size());
|
kdbgln("exec: copying argv to image memory (argc = %zu)", argv.size());
|
||||||
|
|
||||||
u64 user_argv = TRY(copy_string_vector_to_userspace(argv, *image));
|
u64 user_argv = TRY(image->push_string_vector_on_stack(argv));
|
||||||
usize user_argc = argv.size();
|
usize user_argc = argv.size();
|
||||||
|
|
||||||
|
kdbgln("exec: copying envp to image memory (envc = %zu)", envp.size());
|
||||||
|
|
||||||
|
u64 user_envp = TRY(image->push_string_vector_on_stack(envp));
|
||||||
|
usize user_envc = envp.size();
|
||||||
|
|
||||||
// From now on, nothing should fail.
|
// From now on, nothing should fail.
|
||||||
|
|
||||||
kinfoln("exec: image load ok, will now replace existing process image");
|
kinfoln("exec: image load ok, will now replace existing process image");
|
||||||
@ -92,7 +83,7 @@ Result<u64> sys_exec(Registers* regs, SyscallArgs args)
|
|||||||
|
|
||||||
MMU::switch_page_directory(current->directory);
|
MMU::switch_page_directory(current->directory);
|
||||||
|
|
||||||
current->set_arguments(user_argc, user_argv, 0, 0);
|
current->set_arguments(user_argc, user_argv, user_envc, user_envp);
|
||||||
|
|
||||||
memcpy(regs, ¤t->regs, sizeof(*regs));
|
memcpy(regs, ¤t->regs, sizeof(*regs));
|
||||||
|
|
||||||
|
@ -115,13 +115,22 @@ namespace Scheduler
|
|||||||
thread->name = name;
|
thread->name = name;
|
||||||
thread->parent_id = 0;
|
thread->parent_id = 0;
|
||||||
|
|
||||||
|
Vector<String> args;
|
||||||
|
auto name_string = TRY(String::from_cstring(name));
|
||||||
|
TRY(args.try_append(move(name_string)));
|
||||||
|
|
||||||
|
Vector<String> env;
|
||||||
|
|
||||||
auto guard = make_scope_guard([&] { delete thread; });
|
auto guard = make_scope_guard([&] { delete thread; });
|
||||||
|
|
||||||
auto image = TRY(ThreadImage::try_load_from_elf(inode));
|
auto image = TRY(ThreadImage::try_load_from_elf(inode));
|
||||||
|
u64 argv = TRY(image->push_string_vector_on_stack(args));
|
||||||
|
u64 envp = TRY(image->push_string_vector_on_stack(env));
|
||||||
|
|
||||||
guard.deactivate();
|
guard.deactivate();
|
||||||
|
|
||||||
image->apply(thread);
|
image->apply(thread);
|
||||||
|
thread->set_arguments(args.size(), argv, env.size(), envp);
|
||||||
|
|
||||||
kinfoln("Created userspace thread: id %lu with ip %#.16lx and sp %#.16lx (ksp %#lx)", thread->id, thread->ip(),
|
kinfoln("Created userspace thread: id %lu with ip %#.16lx and sp %#.16lx (ksp %#lx)", thread->id, thread->ip(),
|
||||||
thread->sp(), thread->kernel_stack.top());
|
thread->sp(), thread->kernel_stack.top());
|
||||||
|
@ -92,6 +92,21 @@ Result<u64> ThreadImage::push_mem_on_stack(const u8* mem, usize size)
|
|||||||
return m_sp;
|
return m_sp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result<u64> ThreadImage::push_string_vector_on_stack(const Vector<String>& vec)
|
||||||
|
{
|
||||||
|
Vector<u64> user_vec;
|
||||||
|
for (const auto& item : vec)
|
||||||
|
{
|
||||||
|
// Copy each individual string and retrieve a userspace pointer to said copy
|
||||||
|
u64 addr = TRY(push_mem_on_stack((const u8*)item.chars(), item.length() + 1));
|
||||||
|
TRY(user_vec.try_append(addr));
|
||||||
|
}
|
||||||
|
|
||||||
|
TRY(user_vec.try_append((u64) nullptr));
|
||||||
|
// Copy the actual vector of userspace pointers to the stack
|
||||||
|
return TRY(push_mem_on_stack((u8*)user_vec.data(), user_vec.size() * sizeof(u64)));
|
||||||
|
}
|
||||||
|
|
||||||
void ThreadImage::apply(Thread* thread)
|
void ThreadImage::apply(Thread* thread)
|
||||||
{
|
{
|
||||||
thread->init_regs_user();
|
thread->init_regs_user();
|
||||||
|
@ -10,6 +10,8 @@
|
|||||||
#include <luna/OwnedPtr.h>
|
#include <luna/OwnedPtr.h>
|
||||||
#include <luna/Result.h>
|
#include <luna/Result.h>
|
||||||
#include <luna/Stack.h>
|
#include <luna/Stack.h>
|
||||||
|
#include <luna/String.h>
|
||||||
|
#include <luna/Vector.h>
|
||||||
|
|
||||||
class Thread;
|
class Thread;
|
||||||
|
|
||||||
@ -21,6 +23,7 @@ class ThreadImage
|
|||||||
static Result<OwnedPtr<ThreadImage>> clone_from_thread(Thread* parent);
|
static Result<OwnedPtr<ThreadImage>> clone_from_thread(Thread* parent);
|
||||||
|
|
||||||
Result<u64> push_mem_on_stack(const u8* mem, usize size);
|
Result<u64> push_mem_on_stack(const u8* mem, usize size);
|
||||||
|
Result<u64> push_string_vector_on_stack(const Vector<String>& vec);
|
||||||
|
|
||||||
void apply(Thread* thread);
|
void apply(Thread* thread);
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ set(SOURCES
|
|||||||
src/init.cpp
|
src/init.cpp
|
||||||
src/dirent.cpp
|
src/dirent.cpp
|
||||||
src/setjmp.cpp
|
src/setjmp.cpp
|
||||||
|
src/env.cpp
|
||||||
src/sys/stat.cpp
|
src/sys/stat.cpp
|
||||||
src/sys/mman.cpp
|
src/sys/mman.cpp
|
||||||
src/sys/wait.cpp
|
src/sys/wait.cpp
|
||||||
|
@ -102,7 +102,8 @@ extern "C"
|
|||||||
|
|
||||||
int system(const char*);
|
int system(const char*);
|
||||||
|
|
||||||
char* getenv(const char*);
|
/* Get the value of an environment variable. */
|
||||||
|
char* getenv(const char* key);
|
||||||
|
|
||||||
void qsort(void*, size_t, size_t, int (*)(const void*, const void*));
|
void qsort(void*, size_t, size_t, int (*)(const void*, const void*));
|
||||||
void* bsearch(const void*, const void*, size_t, size_t, int (*)(const void*, const void*));
|
void* bsearch(const void*, const void*, size_t, size_t, int (*)(const void*, const void*));
|
||||||
|
@ -34,8 +34,14 @@ extern "C"
|
|||||||
/* Replace the current process with another one. On success, does not return. */
|
/* Replace the current process with another one. On success, does not return. */
|
||||||
int execl(const char* path, const char* arg, ...);
|
int execl(const char* path, const char* arg, ...);
|
||||||
|
|
||||||
int execve(const char*, char* const*, char* const*);
|
/* Replace the current process with another one. On success, does not return. */
|
||||||
int execvp(const char*, char* const*);
|
int execve(const char* path, char* const* argv, char* const* envp);
|
||||||
|
|
||||||
|
/* Replace the current process with another one. On success, does not return. */
|
||||||
|
int execvp(const char* name, char* const* argv);
|
||||||
|
|
||||||
|
/* Replace the current process with another one. On success, does not return. */
|
||||||
|
int execvpe(const char* name, char* const* argv, char* const* envp);
|
||||||
|
|
||||||
/* Call the operating system kernel for a specific service. */
|
/* Call the operating system kernel for a specific service. */
|
||||||
long syscall(long num, ...);
|
long syscall(long num, ...);
|
||||||
|
@ -12,12 +12,14 @@ _start:
|
|||||||
|
|
||||||
pushq %rsi
|
pushq %rsi
|
||||||
pushq %rdi
|
pushq %rdi
|
||||||
|
pushq %rcx
|
||||||
|
|
||||||
call libc_init
|
call libc_init
|
||||||
|
|
||||||
# Run the global constructors.
|
# Run the global constructors.
|
||||||
call _init
|
call _init
|
||||||
|
|
||||||
|
popq %rdx
|
||||||
popq %rdi
|
popq %rdi
|
||||||
popq %rsi
|
popq %rsi
|
||||||
|
|
||||||
|
31
libc/src/env.cpp
Normal file
31
libc/src/env.cpp
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
char** environ = nullptr;
|
||||||
|
|
||||||
|
char* getenv(const char* key)
|
||||||
|
{
|
||||||
|
char** env = environ;
|
||||||
|
size_t len = strlen(key);
|
||||||
|
|
||||||
|
while (*env)
|
||||||
|
{
|
||||||
|
// Retrieve an environment variable from environ.
|
||||||
|
char* var = *(env++);
|
||||||
|
|
||||||
|
// Check for an equals sign, else we just skip this one.
|
||||||
|
char* delim = strchr(var, '=');
|
||||||
|
if (!delim) continue;
|
||||||
|
|
||||||
|
// Get the length of this variable's key (the part before the equals sign)
|
||||||
|
size_t key_length = strcspn(var, "=");
|
||||||
|
if (len != key_length) continue;
|
||||||
|
|
||||||
|
// If the keys match, we found it! Return the value (which is just after the equals sign)
|
||||||
|
if (!memcmp(key, var, key_length)) { return delim + 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
@ -1,10 +1,16 @@
|
|||||||
|
#include <luna/Ignore.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
extern char** environ;
|
||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
{
|
{
|
||||||
void libc_init()
|
void libc_init(int argc, char** argv, int envc, char** envp)
|
||||||
{
|
{
|
||||||
|
ignore(argc, argv, envc);
|
||||||
|
environ = envp;
|
||||||
|
|
||||||
stdin = fdopen(STDIN_FILENO, "r");
|
stdin = fdopen(STDIN_FILENO, "r");
|
||||||
stdout = fdopen(STDOUT_FILENO, "w");
|
stdout = fdopen(STDOUT_FILENO, "w");
|
||||||
stderr = fdopen(STDERR_FILENO, "w");
|
stderr = fdopen(STDERR_FILENO, "w");
|
||||||
|
@ -1,14 +1,18 @@
|
|||||||
|
#include <luna/StringBuilder.h>
|
||||||
#include <luna/Vector.h>
|
#include <luna/Vector.h>
|
||||||
|
|
||||||
#include <bits/errno-return.h>
|
#include <bits/errno-return.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <sys/syscall.h>
|
#include <sys/syscall.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
extern "C" long arch_invoke_syscall(long, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
|
extern "C" long arch_invoke_syscall(long, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
|
||||||
|
|
||||||
|
extern char** environ;
|
||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
{
|
{
|
||||||
pid_t fork(void)
|
pid_t fork(void)
|
||||||
@ -29,10 +33,56 @@ extern "C"
|
|||||||
|
|
||||||
int execv(const char* path, char* const* argv)
|
int execv(const char* path, char* const* argv)
|
||||||
{
|
{
|
||||||
long rc = syscall(SYS_exec, path, argv);
|
return execve(path, argv, environ);
|
||||||
|
}
|
||||||
|
|
||||||
|
int execve(const char* path, char* const* argv, char* const* envp)
|
||||||
|
{
|
||||||
|
long rc = syscall(SYS_execve, path, argv, envp);
|
||||||
__errno_return(rc, int);
|
__errno_return(rc, int);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int execvpe(const char* name, char* const* argv, char* const* envp)
|
||||||
|
{
|
||||||
|
if (strchr(name, '/')) return execve(name, argv, envp);
|
||||||
|
|
||||||
|
char* path = getenv("PATH");
|
||||||
|
if (!path) path = const_cast<char*>("/bin:/sbin");
|
||||||
|
|
||||||
|
Vector<String> paths;
|
||||||
|
bool ok = StringView { path }.split(":").try_move_value_or_error(paths, errno);
|
||||||
|
if (!ok) return -1;
|
||||||
|
|
||||||
|
for (const auto& dir : paths)
|
||||||
|
{
|
||||||
|
// FIXME: Check for errors.
|
||||||
|
StringBuilder sb;
|
||||||
|
sb.add(dir);
|
||||||
|
sb.add('/');
|
||||||
|
sb.add(StringView { name });
|
||||||
|
|
||||||
|
String file;
|
||||||
|
ok = sb.string().try_move_value_or_error(file, errno);
|
||||||
|
if (!ok) return -1;
|
||||||
|
|
||||||
|
int err = errno;
|
||||||
|
execve(file.chars(), argv, envp);
|
||||||
|
|
||||||
|
if (errno != ENOENT && errno != EACCES) return -1;
|
||||||
|
|
||||||
|
// FIXME: POSIX says that if errno == ENOEXEC, we should run /bin/sh <path>.
|
||||||
|
|
||||||
|
if (err == EACCES) errno = err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int execvp(const char* name, char* const* argv)
|
||||||
|
{
|
||||||
|
return execvpe(name, argv, environ);
|
||||||
|
}
|
||||||
|
|
||||||
int execl(const char* path, const char* arg, ...)
|
int execl(const char* path, const char* arg, ...)
|
||||||
{
|
{
|
||||||
va_list ap;
|
va_list ap;
|
||||||
|
@ -26,6 +26,8 @@ class String
|
|||||||
|
|
||||||
Result<String> substring(usize begin, usize size) const;
|
Result<String> substring(usize begin, usize size) const;
|
||||||
|
|
||||||
|
Result<Vector<String>> split(StringView delim) const;
|
||||||
|
|
||||||
static Result<String> format(const String& fmt, ...);
|
static Result<String> format(const String& fmt, ...);
|
||||||
static Result<String> format(StringView fmt, ...);
|
static Result<String> format(StringView fmt, ...);
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <luna/Result.h>
|
#include <luna/Result.h>
|
||||||
|
#include <luna/Vector.h>
|
||||||
|
|
||||||
class String;
|
class String;
|
||||||
|
|
||||||
@ -37,6 +38,8 @@ class StringView
|
|||||||
bool operator==(const char* other) const;
|
bool operator==(const char* other) const;
|
||||||
bool operator==(StringView other) const;
|
bool operator==(StringView other) const;
|
||||||
|
|
||||||
|
Result<Vector<String>> split(StringView delim) const;
|
||||||
|
|
||||||
Iterator begin() const
|
Iterator begin() const
|
||||||
{
|
{
|
||||||
return m_string;
|
return m_string;
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#define enumerate_syscalls(_e) \
|
#define enumerate_syscalls(_e) \
|
||||||
_e(exit) _e(clock_gettime) _e(mmap) _e(munmap) _e(usleep) _e(open) _e(close) _e(read) _e(getpid) _e(write) \
|
_e(exit) _e(clock_gettime) _e(mmap) _e(munmap) _e(usleep) _e(open) _e(close) _e(read) _e(getpid) _e(write) \
|
||||||
_e(lseek) _e(mkdir) _e(exec) _e(mknod) _e(fork) _e(waitpid) _e(getppid) _e(fcntl) _e(getdents)
|
_e(lseek) _e(mkdir) _e(execve) _e(mknod) _e(fork) _e(waitpid) _e(getppid) _e(fcntl) _e(getdents)
|
||||||
|
|
||||||
enum Syscalls
|
enum Syscalls
|
||||||
{
|
{
|
||||||
|
@ -75,6 +75,11 @@ Result<String> String::substring(usize begin, usize size) const
|
|||||||
return String { dup, size };
|
return String { dup, size };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result<Vector<String>> String::split(StringView delim) const
|
||||||
|
{
|
||||||
|
return view().split(delim);
|
||||||
|
}
|
||||||
|
|
||||||
const char& String::operator[](usize index) const
|
const char& String::operator[](usize index) const
|
||||||
{
|
{
|
||||||
expect(index < m_length, "index out of range");
|
expect(index < m_length, "index out of range");
|
||||||
|
@ -46,6 +46,33 @@ bool StringView::operator==(StringView other) const
|
|||||||
return !strcmp(m_string, other.m_string);
|
return !strcmp(m_string, other.m_string);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result<Vector<String>> StringView::split(StringView delim) const
|
||||||
|
{
|
||||||
|
Vector<String> result;
|
||||||
|
String str;
|
||||||
|
|
||||||
|
char* copy = strndup(m_string, m_length);
|
||||||
|
|
||||||
|
char* segment = strtok(copy, delim.chars());
|
||||||
|
if (!segment) goto end;
|
||||||
|
|
||||||
|
str = TRY(String::from_cstring(segment));
|
||||||
|
TRY(result.try_append(move(str)));
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
segment = strtok(nullptr, delim.chars());
|
||||||
|
if (!segment) goto end;
|
||||||
|
|
||||||
|
str = TRY(String::from_cstring(segment));
|
||||||
|
TRY(result.try_append(move(str)));
|
||||||
|
}
|
||||||
|
|
||||||
|
end:
|
||||||
|
free_impl(copy);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
Result<String> StringView::to_string()
|
Result<String> StringView::to_string()
|
||||||
{
|
{
|
||||||
return String::from_cstring(m_string);
|
return String::from_cstring(m_string);
|
||||||
|
Loading…
Reference in New Issue
Block a user