diff --git a/libc/config/linux/api.td b/libc/config/linux/api.td --- a/libc/config/linux/api.td +++ b/libc/config/linux/api.td @@ -157,7 +157,7 @@ SimpleMacroDef<"_IONBF", "2">, SimpleMacroDef<"EOF", "-1">, ]; - let Types = ["size_t", "FILE", "cookie_io_functions_t"]; + let Types = ["off_t", "size_t", "FILE", "cookie_io_functions_t"]; } def StdlibAPI : PublicAPI<"stdlib.h"> { diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -385,6 +385,8 @@ libc.src.stdio.fread libc.src.stdio.fread_unlocked libc.src.stdio.fseek + libc.src.stdio.ftell + libc.src.stdio.ftello libc.src.stdio.funlockfile libc.src.stdio.fwrite libc.src.stdio.fwrite_unlocked diff --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt --- a/libc/include/CMakeLists.txt +++ b/libc/include/CMakeLists.txt @@ -162,6 +162,7 @@ .llvm-libc-macros.stdio_macros .llvm-libc-types.cookie_io_functions_t .llvm-libc-types.FILE + .llvm-libc-types.off_t .llvm-libc-types.size_t ) diff --git a/libc/spec/posix.td b/libc/spec/posix.td --- a/libc/spec/posix.td +++ b/libc/spec/posix.td @@ -998,7 +998,7 @@ HeaderSpec StdIO = HeaderSpec< "stdio.h", [], // Macros - [], // Types + [OffTType], // Types [], // Enumerations [ FunctionSpec< @@ -1011,6 +1011,11 @@ RetValSpec, [ArgSpec] >, + FunctionSpec< + "ftello", + RetValSpec, + [ArgSpec] + >, ] >; diff --git a/libc/spec/stdc.td b/libc/spec/stdc.td --- a/libc/spec/stdc.td +++ b/libc/spec/stdc.td @@ -563,6 +563,11 @@ [ArgSpec, ArgSpec] >, + FunctionSpec< + "ftell", + RetValSpec, + [ArgSpec] + >, FunctionSpec< "putc", RetValSpec, diff --git a/libc/src/__support/File/file.h b/libc/src/__support/File/file.h --- a/libc/src/__support/File/file.h +++ b/libc/src/__support/File/file.h @@ -283,6 +283,10 @@ // library. File *openfile(const char *path, const char *mode); +// The platform_file library should implement it if it relevant for that +// platform. +int get_fileno(File *f); + extern File *stdin; extern File *stdout; extern File *stderr; diff --git a/libc/src/__support/File/linux_file.cpp b/libc/src/__support/File/linux_file.cpp --- a/libc/src/__support/File/linux_file.cpp +++ b/libc/src/__support/File/linux_file.cpp @@ -164,6 +164,11 @@ return file; } +int get_fileno(File *f) { + auto *lf = reinterpret_cast(f); + return lf->get_fd(); +} + constexpr size_t STDIN_BUFFER_SIZE = 512; char stdin_buffer[STDIN_BUFFER_SIZE]; static LinuxFile StdIn(0, stdin_buffer, STDIN_BUFFER_SIZE, _IOFBF, false, diff --git a/libc/src/stdio/CMakeLists.txt b/libc/src/stdio/CMakeLists.txt --- a/libc/src/stdio/CMakeLists.txt +++ b/libc/src/stdio/CMakeLists.txt @@ -416,6 +416,20 @@ libc.src.stdio.printf_core.vfprintf_internal ) +add_entrypoint_object( + ftell + ALIAS + DEPENDS + .${LIBC_TARGET_OS}.ftell +) + +add_entrypoint_object( + ftello + ALIAS + DEPENDS + .${LIBC_TARGET_OS}.ftello +) + add_entrypoint_object( remove ALIAS diff --git a/libc/src/stdio/ftell.h b/libc/src/stdio/ftell.h new file mode 100644 --- /dev/null +++ b/libc/src/stdio/ftell.h @@ -0,0 +1,20 @@ +//===-- Implementation header of ftell --------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC_STDIO_FTELL_H +#define LLVM_LIBC_SRC_STDIO_FTELL_H + +#include + +namespace __llvm_libc { + +long ftell(::FILE *f); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_STDIO_FTELL_H diff --git a/libc/src/stdio/ftello.h b/libc/src/stdio/ftello.h new file mode 100644 --- /dev/null +++ b/libc/src/stdio/ftello.h @@ -0,0 +1,20 @@ +//===-- Implementation header of ftello -------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC_STDIO_FTELLO_H +#define LLVM_LIBC_SRC_STDIO_FTELLO_H + +#include + +namespace __llvm_libc { + +off_t ftello(::FILE *f); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_STDIO_FTELLO_H diff --git a/libc/src/stdio/linux/CMakeLists.txt b/libc/src/stdio/linux/CMakeLists.txt --- a/libc/src/stdio/linux/CMakeLists.txt +++ b/libc/src/stdio/linux/CMakeLists.txt @@ -11,3 +11,40 @@ libc.src.__support.OSUtil.osutil libc.src.errno.errno ) + +add_header_library( + ftell_operation + HDRS + ftell_operation.h + DEPENDS + libc.include.stdio + libc.include.sys_syscall + libc.src.__support.File.file + libc.src.errno.errno +) + +add_entrypoint_object( + ftell + SRCS + ftell.cpp + HDRS + ../ftell.h + DEPENDS + libc.include.stdio + libc.src.__support.OSUtil.osutil + libc.src.__support.File.file + libc.src.__support.File.platform_file +) + +add_entrypoint_object( + ftello + SRCS + ftello.cpp + HDRS + ../ftello.h + DEPENDS + libc.include.stdio + libc.src.__support.OSUtil.osutil + libc.src.__support.File.file + libc.src.__support.File.platform_file +) diff --git a/libc/src/stdio/linux/ftell.cpp b/libc/src/stdio/linux/ftell.cpp new file mode 100644 --- /dev/null +++ b/libc/src/stdio/linux/ftell.cpp @@ -0,0 +1,20 @@ +//===-- Implementation of ftell -------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/stdio/ftell.h" +#include "ftell_operation.h" + +#include + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(long, ftell, (::FILE * stream)) { + return ftell_operation(stream); +} + +} // namespace __llvm_libc diff --git a/libc/src/stdio/linux/ftell_operation.h b/libc/src/stdio/linux/ftell_operation.h new file mode 100644 --- /dev/null +++ b/libc/src/stdio/linux/ftell_operation.h @@ -0,0 +1,42 @@ +//===-- Implementation of linux ftell operation ---------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/__support/File/file.h" + +#include +#include +#include // For syscall numbers. + +namespace __llvm_libc { + +// An implementation of ftell to be used by the C standard ftell and POSIX +// ftello functions. +inline long ftell_operation(FILE *stream) { + File *file = reinterpret_cast<__llvm_libc::File *>(stream); + int fd = __llvm_libc::get_fileno(file); + int whence = SEEK_CUR; + if (file->iseof()) + whence = SEEK_END; + long result; +#ifdef SYS_lseek + long ret = __llvm_libc::syscall_impl(SYS_lseek, fd, 0, whence); + result = ret; +#elif defined(SYS__llseek) + long ret = __llvm_libc::syscall_impl(SYS__llseek, fd, 0, 0, &result, whence); +#else +#error "lseek and _llseek syscalls not available." +#endif + + if (ret < 0) { + errno = -ret; + return -1; + } + return result; +} + +} // namespace __llvm_libc diff --git a/libc/src/stdio/linux/ftello.cpp b/libc/src/stdio/linux/ftello.cpp new file mode 100644 --- /dev/null +++ b/libc/src/stdio/linux/ftello.cpp @@ -0,0 +1,20 @@ +//===-- Implementation of ftello ------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/stdio/ftello.h" +#include "ftell_operation.h" + +#include + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(long, ftello, (::FILE * stream)) { + return ftell_operation(stream); +} + +} // namespace __llvm_libc diff --git a/libc/test/src/stdio/CMakeLists.txt b/libc/test/src/stdio/CMakeLists.txt --- a/libc/test/src/stdio/CMakeLists.txt +++ b/libc/test/src/stdio/CMakeLists.txt @@ -235,6 +235,40 @@ libc.src.stdio.fwrite ) +add_libc_unittest( + ftell_test + SUITE + libc_stdio_unittests + SRCS + ftell_test.cpp + DEPENDS + libc.include.errno + libc.include.stdio + libc.src.stdio.fclose + libc.src.stdio.fflush + libc.src.stdio.fopen + libc.src.stdio.fseek + libc.src.stdio.ftell + libc.src.stdio.fwrite +) + +add_libc_unittest( + ftello_test + SUITE + libc_stdio_unittests + SRCS + ftello_test.cpp + DEPENDS + libc.include.errno + libc.include.stdio + libc.src.stdio.fclose + libc.src.stdio.fflush + libc.src.stdio.fopen + libc.src.stdio.fseek + libc.src.stdio.ftello + libc.src.stdio.fwrite +) + add_subdirectory(printf_core) add_subdirectory(scanf_core) add_subdirectory(testdata) diff --git a/libc/test/src/stdio/ftell_test.cpp b/libc/test/src/stdio/ftell_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/stdio/ftell_test.cpp @@ -0,0 +1,38 @@ +//===-- Unittests for ftell -----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/stdio/fclose.h" +#include "src/stdio/fflush.h" +#include "src/stdio/fopen.h" +#include "src/stdio/fseek.h" +#include "src/stdio/ftell.h" +#include "src/stdio/fwrite.h" +#include "utils/UnitTest/Test.h" + +#include +#include + +TEST(LlvmLibcFtellTest, WriteAndTell) { + constexpr char FILENAME[] = "testdata/ftell.test"; + ::FILE *file = __llvm_libc::fopen(FILENAME, "w"); + ASSERT_FALSE(file == nullptr); + constexpr char CONTENT[] = "123456789"; + constexpr size_t WRITE_SIZE = sizeof(CONTENT) - 1; + ASSERT_EQ(WRITE_SIZE, __llvm_libc::fwrite(CONTENT, 1, WRITE_SIZE, file)); + ASSERT_EQ(0, __llvm_libc::fflush(file)); + + ASSERT_EQ(size_t(__llvm_libc::ftell(file)), WRITE_SIZE); + + long offset = 5; + ASSERT_EQ(0, __llvm_libc::fseek(file, offset, SEEK_SET)); + ASSERT_EQ(__llvm_libc::ftell(file), offset); + ASSERT_EQ(0, __llvm_libc::fseek(file, -offset, SEEK_END)); + ASSERT_EQ(size_t(__llvm_libc::ftell(file)), size_t(WRITE_SIZE - offset)); + + ASSERT_EQ(0, __llvm_libc::fclose(file)); +} diff --git a/libc/test/src/stdio/ftello_test.cpp b/libc/test/src/stdio/ftello_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/stdio/ftello_test.cpp @@ -0,0 +1,38 @@ +//===-- Unittests for ftello ----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/stdio/fclose.h" +#include "src/stdio/fflush.h" +#include "src/stdio/fopen.h" +#include "src/stdio/fseek.h" +#include "src/stdio/ftello.h" +#include "src/stdio/fwrite.h" +#include "utils/UnitTest/Test.h" + +#include +#include + +TEST(LlvmLibcFtelloTest, WriteAndTell) { + constexpr char FILENAME[] = "testdata/ftello.test"; + ::FILE *file = __llvm_libc::fopen(FILENAME, "w"); + ASSERT_FALSE(file == nullptr); + constexpr char CONTENT[] = "123456789"; + constexpr size_t WRITE_SIZE = sizeof(CONTENT) - 1; + ASSERT_EQ(WRITE_SIZE, __llvm_libc::fwrite(CONTENT, 1, WRITE_SIZE, file)); + ASSERT_EQ(0, __llvm_libc::fflush(file)); + + ASSERT_EQ(size_t(__llvm_libc::ftello(file)), WRITE_SIZE); + + long offset = 5; + ASSERT_EQ(0, __llvm_libc::fseek(file, offset, SEEK_SET)); + ASSERT_EQ(__llvm_libc::ftello(file), off_t(offset)); + ASSERT_EQ(0, __llvm_libc::fseek(file, -offset, SEEK_END)); + ASSERT_EQ(__llvm_libc::ftello(file), off_t(WRITE_SIZE - offset)); + + ASSERT_EQ(0, __llvm_libc::fclose(file)); +}