diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt index e78b4d47edd9f..0abd8f6c72a0f 100644 --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -285,6 +285,8 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.stdio.vsnprintf libc.src.stdio.vsprintf libc.src.stdio.vasprintf + libc.src.stdio.getline + libc.src.stdio.getdelim # sys/epoll.h entrypoints libc.src.sys.epoll.epoll_create diff --git a/libc/include/stdio.yaml b/libc/include/stdio.yaml index ef725dd4fe619..9f481b2783a38 100644 --- a/libc/include/stdio.yaml +++ b/libc/include/stdio.yaml @@ -288,6 +288,23 @@ functions: return_type: int arguments: - type: FILE * + - name: getline + standards: + - posix + return_type: ssize_t + arguments: + - type: char **__restrict + - type: size_t *__restrict + - type: FILE * + - name: getdelim + standards: + - posix + return_type: ssize_t + arguments: + - type: char **__restrict + - type: size_t *__restrict + - type: int + - type: FILE * - name: getchar standards: - stdc diff --git a/libc/src/stdio/CMakeLists.txt b/libc/src/stdio/CMakeLists.txt index 3ef6e4f7b7d01..52d874460df4c 100644 --- a/libc/src/stdio/CMakeLists.txt +++ b/libc/src/stdio/CMakeLists.txt @@ -40,6 +40,38 @@ add_entrypoint_object( libc.src.__support.File.platform_file ) +add_entrypoint_object( + getline + SRCS + getline.cpp + HDRS + getline.h + inline_getline.h + DEPENDS + libc.hdr.func.malloc + libc.hdr.func.realloc + libc.hdr.types.FILE + libc.hdr.types.ssize_t + libc.src.__support.File.file + libc.src.__support.libc_errno +) + +add_entrypoint_object( + getdelim + SRCS + getdelim.cpp + HDRS + getdelim.h + inline_getline.h + DEPENDS + libc.hdr.func.malloc + libc.hdr.func.realloc + libc.hdr.types.FILE + libc.hdr.types.ssize_t + libc.src.__support.File.file + libc.src.__support.libc_errno +) + add_entrypoint_object( funlockfile SRCS diff --git a/libc/src/stdio/getdelim.cpp b/libc/src/stdio/getdelim.cpp new file mode 100644 index 0000000000000..2e50f20da69e9 --- /dev/null +++ b/libc/src/stdio/getdelim.cpp @@ -0,0 +1,17 @@ +#include "src/stdio/getdelim.h" + +#include "hdr/types/FILE.h" +#include "hdr/types/size_t.h" +#include "hdr/types/ssize_t.h" +#include "src/__support/File/file.h" +#include "src/__support/macros/attributes.h" +#include "src/__support/macros/config.h" +#include "src/stdio/inline_getline.h" + +namespace LIBC_NAMESPACE_DECL { + + LLVM_LIBC_FUNCTION(ssize_t, getdelim, (char **__restrict lineptr, size_t *__restrict n, + int delimiter, ::FILE *__restrict stream)) { + return LIBC_NAMESPACE::__getline(lineptr, n, delimiter, stream); +} +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/stdio/getdelim.h b/libc/src/stdio/getdelim.h new file mode 100644 index 0000000000000..050e8bca13c44 --- /dev/null +++ b/libc/src/stdio/getdelim.h @@ -0,0 +1,14 @@ +#ifndef LLVM_LIBC_SRC_STDIO_GETDELIM_H +#define LLVM_LIBC_SRC_STDIO_GETDELIM_H + +#include "hdr/types/FILE.h" +#include "hdr/types/size_t.h" +#include "hdr/types/ssize_t.h" +#include "src/__support/macros/config.h" + +namespace LIBC_NAMESPACE_DECL { + +ssize_t getdelim(char **__restrict lineptr, size_t *__restrict n, int delimiter, + ::FILE *__restrict stream); +} // namespace LIBC_NAMESPACE_DECL +#endif diff --git a/libc/src/stdio/getline.cpp b/libc/src/stdio/getline.cpp new file mode 100644 index 0000000000000..d4524a5eb6c94 --- /dev/null +++ b/libc/src/stdio/getline.cpp @@ -0,0 +1,19 @@ +#include "src/stdio/getline.h" + +#include "hdr/types/FILE.h" +#include "hdr/types/size_t.h" +#include "hdr/types/ssize_t.h" +#include "src/__support/File/file.h" +#include "src/__support/macros/attributes.h" +#include "src/__support/macros/config.h" +#include "src/stdio/inline_getline.h" + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(ssize_t, getline, + (char **__restrict lineptr, size_t *__restrict n, + ::FILE *__restrict stream)) { + return LIBC_NAMESPACE::__getline(lineptr, n, '\n', stream); +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/stdio/getline.h b/libc/src/stdio/getline.h new file mode 100644 index 0000000000000..de090b3a93780 --- /dev/null +++ b/libc/src/stdio/getline.h @@ -0,0 +1,16 @@ +#ifndef LLVM_LIBC_SRC_STDIO_GETLINE_H +#define LLVM_LIBC_SRC_STDIO_GETLINE_H + +#include "hdr/types/FILE.h" +#include "hdr/types/size_t.h" +#include "hdr/types/ssize_t.h" +#include "src/__support/macros/config.h" + +namespace LIBC_NAMESPACE_DECL { + +ssize_t getline(char **__restrict lineptr, size_t *__restrict n, + ::FILE *__restrict stream); + +} // namespace LIBC_NAMESPACE_DECL + +#endif diff --git a/libc/src/stdio/inline_getline.h b/libc/src/stdio/inline_getline.h new file mode 100644 index 0000000000000..0cae435312222 --- /dev/null +++ b/libc/src/stdio/inline_getline.h @@ -0,0 +1,72 @@ +#ifndef LLVM_LIBC_SRC_STDIO_INLINE_GETLINE_H +#define LLVM_LIBC_SRC_STDIO_INLINE_GETLINE_H + +#include "hdr/func/free.h" +#include "hdr/func/malloc.h" +#include "hdr/func/realloc.h" +#include "hdr/types/FILE.h" +#include "hdr/types/size_t.h" +#include "hdr/types/ssize_t.h" +#include "src/__support/File/file.h" +#include "src/__support/libc_errno.h" +#include "src/__support/macros/attributes.h" +#include "src/__support/macros/config.h" + +constexpr int INIT_BASE = 32; + +namespace LIBC_NAMESPACE_DECL { + +LIBC_INLINE ssize_t __getline(char **__restrict lineptr, size_t *__restrict n, + int del, ::FILE *__restrict stream) { + if (!lineptr || !n || !stream) { + libc_errno = EINVAL; + return -1; + } + + auto *file = reinterpret_cast(stream); + + if (*lineptr == nullptr) { + *n = (*n == 0) ? INIT_BASE : *n; + *lineptr = static_cast(malloc(*n)); + if (!*lineptr) { + libc_errno = ENOMEM; + return -1; + } + } + uint8_t c = 0; + size_t bytes_read = 0; + file->lock(); + while (true) { + auto result = file->read_unlocked(&c, 1); + if (result.has_error()) { + file->unlock(); + libc_errno = result.error; + return EOF; + } + if (result.value != 1) + break; + if ((bytes_read + 2) > *n) { + size_t new_size = + ((bytes_read + 2) >= (*n * 2)) ? bytes_read + 2 : *n * 2; + char *tmpptr = static_cast(realloc(*lineptr, new_size)); + if (!tmpptr) { + file->unlock(); + libc_errno = ENOMEM; + return -1; + } + *lineptr = tmpptr; + *n = new_size; + } + (*lineptr)[bytes_read++] = static_cast(c); + + if (c == del) + break; + } + file->unlock(); + if (bytes_read == 0) + return -1; + (*lineptr)[bytes_read] = '\0'; + return bytes_read; +} +} // namespace LIBC_NAMESPACE_DECL +#endif diff --git a/libc/utils/docgen/stdio.yaml b/libc/utils/docgen/stdio.yaml index 9dabee44a51c8..a3e419fe3b2dc 100644 --- a/libc/utils/docgen/stdio.yaml +++ b/libc/utils/docgen/stdio.yaml @@ -207,9 +207,9 @@ functions: funlockfile: in-latest-posix: '' getdelim: - in-latest-posix: '' + in-latest-posix: 'YES' getline: - in-latest-posix: '' + in-latest-posix: 'YES' open_memstream: in-latest-posix: '' pclose: