Skip to content
9 changes: 4 additions & 5 deletions builtin-functions/kphp-light/stdlib/file-functions.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,17 +40,18 @@ function vsprintf ($format ::: string, $args ::: array) ::: string;

function file_exists ($name ::: string) ::: bool;

// === UNSUPPORTED ===
function file ($name ::: string) ::: string[] | false;

function is_file ($name ::: string) ::: bool;

// === UNSUPPORTED ===
/** @kphp-extern-func-info stub generation-required */
function chmod ($name ::: string, $mode ::: int) ::: bool;
/** @kphp-extern-func-info stub generation-required */
function copy ($from ::: string, $to ::: string) ::: bool;
/** @kphp-extern-func-info stub generation-required */
function dirname ($name ::: string) ::: string;
/** @kphp-extern-func-info stub generation-required */
function file ($name ::: string) ::: string[] | false;
/** @kphp-extern-func-info stub generation-required */
function file_put_contents ($name ::: string, $content ::: mixed, $flags ::: int = 0) ::: int | false;
/** @kphp-extern-func-info stub generation-required */
function filectime ($name ::: string) ::: int | false;
Expand All @@ -59,8 +60,6 @@ function filemtime ($name ::: string) ::: int | false;
/** @kphp-extern-func-info stub generation-required */
function is_dir ($name ::: string) ::: bool;
/** @kphp-extern-func-info stub generation-required */
function is_file ($name ::: string) ::: bool;
/** @kphp-extern-func-info stub generation-required */
function is_readable ($name ::: string) ::: bool;
/** @kphp-extern-func-info stub generation-required */
function mkdir ($name ::: string, $mode ::: int = 0777, $recursive ::: bool = false) ::: bool;
Expand Down
7 changes: 7 additions & 0 deletions runtime-light/k2-platform/k2-api.h
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,13 @@ inline int32_t stat(std::string_view path, struct stat* stat) noexcept {
return k2::errno_ok;
}

inline int32_t lstat(std::string_view path, struct stat* stat) noexcept {
if (auto error_code{k2_lstat(path.data(), path.size(), stat)}; error_code != k2::errno_ok) [[unlikely]] {
return error_code;
}
return k2::errno_ok;
}

using CommandArg = CommandArg;

enum class CommandStdoutPolicy : uint8_t { NoCapture, Capture };
Expand Down
23 changes: 23 additions & 0 deletions runtime-light/k2-platform/k2-header.h
Original file line number Diff line number Diff line change
Expand Up @@ -735,6 +735,29 @@ int32_t k2_canonicalize(const char* path, size_t pathlen, char* const* resolved_
*/
int32_t k2_stat(const char* pathname, size_t pathname_len, struct stat* statbuf);

/**
* Semantically equivalent to libc's `lstat`.
*
* Possible `errno`:
* `EACCES` => Search permission is denied for one of the directories in the path prefix of `pathname`.
* `EINVAL` => `pathname` or `statbuf` is `NULL`.
* `EFAULT` => Bad address.
* `ELOOP` => Too many symbolic links encountered while traversing the path.
* `ENAMETOOLONG` => `pathname` is too long.
* `ENOENT` => A component of `pathname` does not exist or is a dangling symbolic link.
* `ENOMEM` => Out of memory (i.e., kernel memory).
* `ENOTDIR` => A component of the path prefix of `pathname` is not a directory.
* `EOVERFLOW` => `pathname` refers to a file whose size, inode number,
* or number of blocks cannot be represented in, respectively,
* the types `off_t`, `ino_t`, or `blkcnt_t`. This error can occur
* when, for example, an application compiled on a 32-bit
* platform without `-D_FILE_OFFSET_BITS=64` calls `lstat()` on a
* file whose size exceeds `(1<<31)-1` bytes.
* `ERANGE` => Failed to convert `st_size`, `st_blksize`, or `st_blocks` to `int64_t`.
* `ENOSYS` => Internal error.
*/
int32_t k2_lstat(const char* pathname, size_t pathname_len, struct stat* statbuf);

struct CommandArg {
const char* arg;
size_t arg_len;
Expand Down
52 changes: 52 additions & 0 deletions runtime-light/stdlib/file/file-system-functions.h
Original file line number Diff line number Diff line change
Expand Up @@ -197,3 +197,55 @@ inline Optional<string> f$file_get_contents(const string& stream) noexcept {
}
return false;
}

inline Optional<array<string>> f$file(const string& name) noexcept {
struct stat stat_buf;
uint64_t file_fd;

if (k2::fopen(std::addressof(file_fd), name.c_str(), "r") != k2::errno_ok) {
return false;
}
if (k2::stat(name.c_str(), std::addressof(stat_buf)) != k2::errno_ok) {
k2::free_descriptor(file_fd);
return false;
}
if (!S_ISREG(stat_buf.st_mode)) {
k2::free_descriptor(file_fd);
kphp::log::warning("regular file expected as first argument in function file, \"{}\" is given", name.c_str());
return false;
}

const size_t size{static_cast<size_t>(stat_buf.st_size)};
if (size > string::max_size()) {
k2::free_descriptor(file_fd);
kphp::log::warning("file \"{}\" is too large", name.c_str());
return false;
}

string res(static_cast<string::size_type>(size), false);
char* s{res.buffer()};
if (k2::read(file_fd, size, s) < size) {
k2::free_descriptor(file_fd);
return false;
}
k2::free_descriptor(file_fd);

array<string> result;
int prev{-1};
for (size_t i = 0; i < size; i++) {
if (s[i] == '\n' || i + 1 == size) {
result.push_back(string(s + prev + 1, i - prev));
prev = i;
}
}

return result;
}

inline bool f$is_file(const string& name) noexcept {
struct stat stat_buf;
if (k2::lstat(name.c_str(), std::addressof(stat_buf)) != k2::errno_ok) {
return false;
}
return S_ISREG(stat_buf.st_mode);
}
Loading