libc.src.stdio.fread
libc.src.stdio.fread_unlocked
libc.src.stdio.fseek
+ libc.src.stdio.ftell
libc.src.stdio.funlockfile
libc.src.stdio.fwrite
libc.src.stdio.fwrite_unlocked
.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
)
ArgSpec<FILEPtr>]
>,
FunctionSpec<
+ "ftell",
+ RetValSpec<LongType>,
+ [ArgSpec<FILEPtr>]
+ >,
+ FunctionSpec<
"putc",
RetValSpec<IntType>,
[ArgSpec<IntType>,
// Reset the eof flag as a seek might move the file positon to some place
// readable.
eof = false;
- return platform_seek(this, offset, whence);
+ long platform_pos = platform_seek(this, offset, whence);
+ if (platform_pos >= 0)
+ return 0;
+ else
+ return -1;
+}
+
+long File::tell() {
+ FileLock lock(this);
+ long platform_offset;
+ if (eof)
+ platform_offset = platform_seek(this, 0, SEEK_END);
+ else
+ platform_offset = platform_seek(this, 0, SEEK_CUR);
+ if (platform_offset < 0)
+ return -1;
+ if (prev_op == FileOp::READ)
+ return platform_offset - (read_limit - pos);
+ else if (prev_op == FileOp::WRITE)
+ return platform_offset + pos;
+ else
+ return platform_offset;
}
int File::flush_unlocked() {
using WriteFunc = size_t(File *, const void *, size_t);
using ReadFunc = size_t(File *, void *, size_t);
- using SeekFunc = int(File *, long, int);
+ // The SeekFunc is expected to return the current offset of the external
+ // file position indicator.
+ using SeekFunc = long(File *, long, int);
using CloseFunc = int(File *);
using FlushFunc = int(File *);
int seek(long offset, int whence);
+ long tell();
+
// If buffer has data written to it, flush it out. Does nothing if the
// buffer is currently being used as a read buffer.
int flush() {
// 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;
size_t write_func(File *, const void *, size_t);
size_t read_func(File *, void *, size_t);
-int seek_func(File *, long, int);
+long seek_func(File *, long, int);
int close_func(File *);
int flush_func(File *);
return ret;
}
-int seek_func(File *f, long offset, int whence) {
+long seek_func(File *f, long offset, int whence) {
auto *lf = reinterpret_cast<LinuxFile *>(f);
+ long result;
#ifdef SYS_lseek
long ret = __llvm_libc::syscall_impl(SYS_lseek, lf->get_fd(), offset, whence);
+ result = ret;
#elif defined(SYS__llseek)
long result;
long ret = __llvm_libc::syscall_impl(SYS__llseek, lf->get_fd(), offset >> 32,
errno = -ret;
return -1;
}
- return 0;
+ return result;
}
int close_func(File *f) {
return file;
}
+int get_fileno(File *f) {
+ auto *lf = reinterpret_cast<LinuxFile *>(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,
)
add_entrypoint_object(
+ ftell
+ SRCS
+ ftell.cpp
+ HDRS
+ ftell.h
+ DEPENDS
+ libc.include.stdio
+ libc.src.__support.File.file
+ libc.src.__support.File.platform_file
+)
+
+add_entrypoint_object(
remove
ALIAS
DEPENDS
reinterpret_cast<char *>(data), size);
}
-int seek_func(File *f, long offset, int whence) {
+long seek_func(File *f, long offset, int whence) {
auto cookie_file = reinterpret_cast<CookieFile *>(f);
if (cookie_file->ops.seek == nullptr) {
errno = EINVAL;
return -1;
}
off64_t offset64 = offset;
- return cookie_file->ops.seek(cookie_file->cookie, &offset64, whence);
+ int result = cookie_file->ops.seek(cookie_file->cookie, &offset64, whence);
+ if (result == 0)
+ return offset64;
+ else
+ return -1;
}
int close_func(File *f) {
--- /dev/null
+//===-- 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 "src/__support/File/file.h"
+
+#include <stdio.h>
+
+namespace __llvm_libc {
+
+LLVM_LIBC_FUNCTION(long, ftell, (::FILE * stream)) {
+ return reinterpret_cast<__llvm_libc::File *>(stream)->tell();
+}
+
+} // namespace __llvm_libc
--- /dev/null
+//===-- 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 <stdio.h>
+
+namespace __llvm_libc {
+
+long ftell(::FILE *f);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_STDIO_FTELL_H
static size_t str_read(__llvm_libc::File *f, void *data, size_t len);
static size_t str_write(__llvm_libc::File *f, const void *data, size_t len);
- static int str_seek(__llvm_libc::File *f, long offset, int whence);
+ static long str_seek(__llvm_libc::File *f, long offset, int whence);
static int str_close(__llvm_libc::File *f) { return 0; }
static int str_flush(__llvm_libc::File *f) { return 0; }
return i;
}
-int StringFile::str_seek(__llvm_libc::File *f, long offset, int whence) {
+long StringFile::str_seek(__llvm_libc::File *f, long offset, int whence) {
StringFile *sf = static_cast<StringFile *>(f);
if (whence == SEEK_SET)
sf->pos = offset;
sf->pos += offset;
if (whence == SEEK_END)
sf->pos = SIZE + offset;
- return 0;
+ return sf->pos;
}
StringFile *new_string_file(char *buffer, size_t buflen, int bufmode,
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.fread
+ libc.src.stdio.fseek
+ libc.src.stdio.ftell
+ libc.src.stdio.fwrite
+ libc.src.stdio.setvbuf
+)
+
add_subdirectory(printf_core)
add_subdirectory(scanf_core)
add_subdirectory(testdata)
--- /dev/null
+//===-- 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/fread.h"
+#include "src/stdio/fseek.h"
+#include "src/stdio/ftell.h"
+#include "src/stdio/fwrite.h"
+#include "src/stdio/setvbuf.h"
+#include "utils/UnitTest/Test.h"
+
+#include <stdio.h>
+
+class LlvmLibcFTellTest : public __llvm_libc::testing::Test {
+protected:
+ void test_with_bufmode(int bufmode) {
+ constexpr char FILENAME[] = "testdata/ftell.test";
+ // We will set a special buffer to the file so that we guarantee buffering.
+ constexpr size_t BUFFER_SIZE = 1024;
+ char buffer[BUFFER_SIZE];
+ ::FILE *file = __llvm_libc::fopen(FILENAME, "w+");
+ ASSERT_FALSE(file == nullptr);
+ ASSERT_EQ(__llvm_libc::setvbuf(file, buffer, bufmode, BUFFER_SIZE), 0);
+
+ // Include few '\n' chars to test when |bufmode| is _IOLBF.
+ constexpr char CONTENT[] = "12\n345\n6789";
+ constexpr size_t WRITE_SIZE = sizeof(CONTENT) - 1;
+ ASSERT_EQ(WRITE_SIZE, __llvm_libc::fwrite(CONTENT, 1, WRITE_SIZE, file));
+ // The above write should have buffered the written data and not have
+ // trasferred it to the underlying stream. But, ftell operation should
+ // still return the correct effective offset.
+ 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::fseek(file, 0, SEEK_SET));
+ constexpr size_t READ_SIZE = WRITE_SIZE / 2;
+ char data[READ_SIZE];
+ // Reading a small amount will actually read out much more data and
+ // buffer it. But, ftell should return the correct effective offset.
+ ASSERT_EQ(READ_SIZE, __llvm_libc::fread(data, 1, READ_SIZE, file));
+ ASSERT_EQ(size_t(__llvm_libc::ftell(file)), READ_SIZE);
+
+ ASSERT_EQ(0, __llvm_libc::fclose(file));
+ }
+};
+
+TEST_F(LlvmLibcFTellTest, TellWithFBF) { test_with_bufmode(_IOFBF); }
+
+TEST_F(LlvmLibcFTellTest, TellWithNBF) { test_with_bufmode(_IONBF); }
+
+TEST_F(LlvmLibcFTellTest, TellWithLBF) { test_with_bufmode(_IOLBF); }