mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 01:37:35 +00:00
LibC: Implement {f,}getwc()
This commit is contained in:
parent
3b281baf75
commit
cf17677206
5 changed files with 207 additions and 130 deletions
|
@ -66,6 +66,7 @@ set(LIBC_SOURCES
|
||||||
utsname.cpp
|
utsname.cpp
|
||||||
wchar.cpp
|
wchar.cpp
|
||||||
wctype.cpp
|
wctype.cpp
|
||||||
|
wstdio.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
file(GLOB AK_SOURCES CONFIGURE_DEPENDS "../../../AK/*.cpp")
|
file(GLOB AK_SOURCES CONFIGURE_DEPENDS "../../../AK/*.cpp")
|
||||||
|
|
141
Userland/Libraries/LibC/bits/stdio_file_implementation.h
Normal file
141
Userland/Libraries/LibC/bits/stdio_file_implementation.h
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021, the SerenityOS developers.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <AK/Types.h>
|
||||||
|
#include <LibC/bits/FILE.h>
|
||||||
|
#include <LibC/bits/pthread_integration.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
struct FILE {
|
||||||
|
public:
|
||||||
|
FILE(int fd, int mode)
|
||||||
|
: m_fd(fd)
|
||||||
|
, m_mode(mode)
|
||||||
|
{
|
||||||
|
__pthread_mutex_init(&m_mutex, nullptr);
|
||||||
|
}
|
||||||
|
~FILE();
|
||||||
|
|
||||||
|
static FILE* create(int fd, int mode);
|
||||||
|
|
||||||
|
void setbuf(u8* data, int mode, size_t size) { m_buffer.setbuf(data, mode, size); }
|
||||||
|
|
||||||
|
bool flush();
|
||||||
|
void purge();
|
||||||
|
bool close();
|
||||||
|
|
||||||
|
int fileno() const { return m_fd; }
|
||||||
|
bool eof() const { return m_eof; }
|
||||||
|
int mode() const { return m_mode; }
|
||||||
|
u8 flags() const { return m_flags; }
|
||||||
|
|
||||||
|
int error() const { return m_error; }
|
||||||
|
void clear_err() { m_error = 0; }
|
||||||
|
|
||||||
|
size_t read(u8*, size_t);
|
||||||
|
size_t write(const u8*, size_t);
|
||||||
|
|
||||||
|
bool gets(u8*, size_t);
|
||||||
|
bool ungetc(u8 byte) { return m_buffer.enqueue_front(byte); }
|
||||||
|
|
||||||
|
int seek(off_t offset, int whence);
|
||||||
|
off_t tell();
|
||||||
|
|
||||||
|
pid_t popen_child() { return m_popen_child; }
|
||||||
|
void set_popen_child(pid_t child_pid) { m_popen_child = child_pid; }
|
||||||
|
|
||||||
|
void reopen(int fd, int mode);
|
||||||
|
|
||||||
|
enum Flags : u8 {
|
||||||
|
None = 0,
|
||||||
|
LastRead = 1,
|
||||||
|
LastWrite = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Buffer {
|
||||||
|
// A ringbuffer that also transparently implements ungetc().
|
||||||
|
public:
|
||||||
|
~Buffer();
|
||||||
|
|
||||||
|
int mode() const { return m_mode; }
|
||||||
|
void setbuf(u8* data, int mode, size_t size);
|
||||||
|
// Make sure to call realize() before enqueuing any data.
|
||||||
|
// Dequeuing can be attempted without it.
|
||||||
|
void realize(int fd);
|
||||||
|
void drop();
|
||||||
|
|
||||||
|
bool may_use() const { return m_ungotten || m_mode != _IONBF; }
|
||||||
|
bool is_not_empty() const { return m_ungotten || !m_empty; }
|
||||||
|
size_t buffered_size() const;
|
||||||
|
|
||||||
|
const u8* begin_dequeue(size_t& available_size) const;
|
||||||
|
void did_dequeue(size_t actual_size);
|
||||||
|
|
||||||
|
u8* begin_enqueue(size_t& available_size) const;
|
||||||
|
void did_enqueue(size_t actual_size);
|
||||||
|
|
||||||
|
bool enqueue_front(u8 byte);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Note: the fields here are arranged this way
|
||||||
|
// to make sizeof(Buffer) smaller.
|
||||||
|
u8* m_data { nullptr };
|
||||||
|
size_t m_capacity { BUFSIZ };
|
||||||
|
size_t m_begin { 0 };
|
||||||
|
size_t m_end { 0 };
|
||||||
|
|
||||||
|
int m_mode { -1 };
|
||||||
|
u8 m_unget_buffer { 0 };
|
||||||
|
bool m_ungotten : 1 { false };
|
||||||
|
bool m_data_is_malloced : 1 { false };
|
||||||
|
// When m_begin == m_end, we want to distinguish whether
|
||||||
|
// the buffer is full or empty.
|
||||||
|
bool m_empty : 1 { true };
|
||||||
|
};
|
||||||
|
|
||||||
|
// Read or write using the underlying fd, bypassing the buffer.
|
||||||
|
ssize_t do_read(u8*, size_t);
|
||||||
|
ssize_t do_write(const u8*, size_t);
|
||||||
|
|
||||||
|
// Read some data into the buffer.
|
||||||
|
bool read_into_buffer();
|
||||||
|
// Flush *some* data from the buffer.
|
||||||
|
bool write_from_buffer();
|
||||||
|
|
||||||
|
void lock();
|
||||||
|
void unlock();
|
||||||
|
|
||||||
|
int m_fd { -1 };
|
||||||
|
int m_mode { 0 };
|
||||||
|
u8 m_flags { Flags::None };
|
||||||
|
int m_error { 0 };
|
||||||
|
bool m_eof { false };
|
||||||
|
pid_t m_popen_child { -1 };
|
||||||
|
Buffer m_buffer;
|
||||||
|
__pthread_mutex_t m_mutex;
|
||||||
|
|
||||||
|
friend class ScopedFileLock;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ScopedFileLock {
|
||||||
|
public:
|
||||||
|
ScopedFileLock(FILE* file)
|
||||||
|
: m_file(file)
|
||||||
|
{
|
||||||
|
m_file->lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
~ScopedFileLock()
|
||||||
|
{
|
||||||
|
m_file->unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
FILE* m_file;
|
||||||
|
};
|
|
@ -10,7 +10,7 @@
|
||||||
#include <AK/ScopedValueRollback.h>
|
#include <AK/ScopedValueRollback.h>
|
||||||
#include <AK/StdLibExtras.h>
|
#include <AK/StdLibExtras.h>
|
||||||
#include <AK/String.h>
|
#include <AK/String.h>
|
||||||
#include <LibC/bits/pthread_integration.h>
|
#include <LibC/bits/stdio_file_implementation.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
@ -25,118 +25,6 @@
|
||||||
#include <syscall.h>
|
#include <syscall.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
struct FILE {
|
|
||||||
public:
|
|
||||||
FILE(int fd, int mode)
|
|
||||||
: m_fd(fd)
|
|
||||||
, m_mode(mode)
|
|
||||||
{
|
|
||||||
__pthread_mutex_init(&m_mutex, nullptr);
|
|
||||||
}
|
|
||||||
~FILE();
|
|
||||||
|
|
||||||
static FILE* create(int fd, int mode);
|
|
||||||
|
|
||||||
void setbuf(u8* data, int mode, size_t size) { m_buffer.setbuf(data, mode, size); }
|
|
||||||
|
|
||||||
bool flush();
|
|
||||||
void purge();
|
|
||||||
bool close();
|
|
||||||
|
|
||||||
int fileno() const { return m_fd; }
|
|
||||||
bool eof() const { return m_eof; }
|
|
||||||
int mode() const { return m_mode; }
|
|
||||||
u8 flags() const { return m_flags; }
|
|
||||||
|
|
||||||
int error() const { return m_error; }
|
|
||||||
void clear_err() { m_error = 0; }
|
|
||||||
|
|
||||||
size_t read(u8*, size_t);
|
|
||||||
size_t write(const u8*, size_t);
|
|
||||||
|
|
||||||
bool gets(u8*, size_t);
|
|
||||||
bool ungetc(u8 byte) { return m_buffer.enqueue_front(byte); }
|
|
||||||
|
|
||||||
int seek(off_t offset, int whence);
|
|
||||||
off_t tell();
|
|
||||||
|
|
||||||
pid_t popen_child() { return m_popen_child; }
|
|
||||||
void set_popen_child(pid_t child_pid) { m_popen_child = child_pid; }
|
|
||||||
|
|
||||||
void reopen(int fd, int mode);
|
|
||||||
|
|
||||||
enum Flags : u8 {
|
|
||||||
None = 0,
|
|
||||||
LastRead = 1,
|
|
||||||
LastWrite = 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
|
||||||
struct Buffer {
|
|
||||||
// A ringbuffer that also transparently implements ungetc().
|
|
||||||
public:
|
|
||||||
~Buffer();
|
|
||||||
|
|
||||||
int mode() const { return m_mode; }
|
|
||||||
void setbuf(u8* data, int mode, size_t size);
|
|
||||||
// Make sure to call realize() before enqueuing any data.
|
|
||||||
// Dequeuing can be attempted without it.
|
|
||||||
void realize(int fd);
|
|
||||||
void drop();
|
|
||||||
|
|
||||||
bool may_use() const { return m_ungotten || m_mode != _IONBF; }
|
|
||||||
bool is_not_empty() const { return m_ungotten || !m_empty; }
|
|
||||||
size_t buffered_size() const;
|
|
||||||
|
|
||||||
const u8* begin_dequeue(size_t& available_size) const;
|
|
||||||
void did_dequeue(size_t actual_size);
|
|
||||||
|
|
||||||
u8* begin_enqueue(size_t& available_size) const;
|
|
||||||
void did_enqueue(size_t actual_size);
|
|
||||||
|
|
||||||
bool enqueue_front(u8 byte);
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Note: the fields here are arranged this way
|
|
||||||
// to make sizeof(Buffer) smaller.
|
|
||||||
u8* m_data { nullptr };
|
|
||||||
size_t m_capacity { BUFSIZ };
|
|
||||||
size_t m_begin { 0 };
|
|
||||||
size_t m_end { 0 };
|
|
||||||
|
|
||||||
int m_mode { -1 };
|
|
||||||
u8 m_unget_buffer { 0 };
|
|
||||||
bool m_ungotten : 1 { false };
|
|
||||||
bool m_data_is_malloced : 1 { false };
|
|
||||||
// When m_begin == m_end, we want to distinguish whether
|
|
||||||
// the buffer is full or empty.
|
|
||||||
bool m_empty : 1 { true };
|
|
||||||
};
|
|
||||||
|
|
||||||
// Read or write using the underlying fd, bypassing the buffer.
|
|
||||||
ssize_t do_read(u8*, size_t);
|
|
||||||
ssize_t do_write(const u8*, size_t);
|
|
||||||
|
|
||||||
// Read some data into the buffer.
|
|
||||||
bool read_into_buffer();
|
|
||||||
// Flush *some* data from the buffer.
|
|
||||||
bool write_from_buffer();
|
|
||||||
|
|
||||||
void lock();
|
|
||||||
void unlock();
|
|
||||||
|
|
||||||
int m_fd { -1 };
|
|
||||||
int m_mode { 0 };
|
|
||||||
u8 m_flags { Flags::None };
|
|
||||||
int m_error { 0 };
|
|
||||||
bool m_eof { false };
|
|
||||||
pid_t m_popen_child { -1 };
|
|
||||||
Buffer m_buffer;
|
|
||||||
__pthread_mutex_t m_mutex;
|
|
||||||
|
|
||||||
friend class ScopedFileLock;
|
|
||||||
};
|
|
||||||
|
|
||||||
FILE::~FILE()
|
FILE::~FILE()
|
||||||
{
|
{
|
||||||
bool already_closed = m_fd == -1;
|
bool already_closed = m_fd == -1;
|
||||||
|
@ -595,23 +483,6 @@ void FILE::unlock()
|
||||||
__pthread_mutex_unlock(&m_mutex);
|
__pthread_mutex_unlock(&m_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
class ScopedFileLock {
|
|
||||||
public:
|
|
||||||
ScopedFileLock(FILE* file)
|
|
||||||
: m_file(file)
|
|
||||||
{
|
|
||||||
m_file->lock();
|
|
||||||
}
|
|
||||||
|
|
||||||
~ScopedFileLock()
|
|
||||||
{
|
|
||||||
m_file->unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
FILE* m_file;
|
|
||||||
};
|
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
||||||
alignas(FILE) static u8 default_streams[3][sizeof(FILE)];
|
alignas(FILE) static u8 default_streams[3][sizeof(FILE)];
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <bits/FILE.h>
|
||||||
|
#include <stdarg.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <sys/cdefs.h>
|
#include <sys/cdefs.h>
|
||||||
|
|
||||||
|
@ -69,4 +71,7 @@ size_t mbsnrtowcs(wchar_t*, const char**, size_t, size_t, mbstate_t*);
|
||||||
size_t wcscspn(const wchar_t* wcs, const wchar_t* reject);
|
size_t wcscspn(const wchar_t* wcs, const wchar_t* reject);
|
||||||
size_t wcsspn(const wchar_t* wcs, const wchar_t* accept);
|
size_t wcsspn(const wchar_t* wcs, const wchar_t* accept);
|
||||||
|
|
||||||
|
wint_t fgetwc(FILE* stream);
|
||||||
|
wint_t getwc(FILE* stream);
|
||||||
|
|
||||||
__END_DECLS
|
__END_DECLS
|
||||||
|
|
59
Userland/Libraries/LibC/wstdio.cpp
Normal file
59
Userland/Libraries/LibC/wstdio.cpp
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021, Ali Mohammad Pur <mpfard@serenityos.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <AK/Assertions.h>
|
||||||
|
#include <AK/StringBuilder.h>
|
||||||
|
#include <AK/Types.h>
|
||||||
|
#include <bits/stdio_file_implementation.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <wchar.h>
|
||||||
|
|
||||||
|
static_assert(AssertSize<wchar_t, sizeof(u32)>());
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
wint_t fgetwc(FILE* stream)
|
||||||
|
{
|
||||||
|
VERIFY(stream);
|
||||||
|
Array<u8, 4> underlying;
|
||||||
|
auto underlying_bytes = underlying.span();
|
||||||
|
size_t encoded_length = 0;
|
||||||
|
size_t bytes_read = 0;
|
||||||
|
do {
|
||||||
|
size_t nread = fread(underlying_bytes.offset_pointer(bytes_read), 1, 1, stream);
|
||||||
|
if (nread != 1) {
|
||||||
|
errno = EILSEQ;
|
||||||
|
return WEOF;
|
||||||
|
}
|
||||||
|
++bytes_read;
|
||||||
|
if (bytes_read == 1) {
|
||||||
|
if (underlying[0] >> 7 == 0) {
|
||||||
|
encoded_length = 1;
|
||||||
|
} else if (underlying[0] >> 5 == 6) {
|
||||||
|
encoded_length = 2;
|
||||||
|
} else if (underlying[0] >> 4 == 0xe) {
|
||||||
|
encoded_length = 3;
|
||||||
|
} else if (underlying[0] >> 3 == 0x1e) {
|
||||||
|
encoded_length = 4;
|
||||||
|
} else {
|
||||||
|
errno = EILSEQ;
|
||||||
|
return WEOF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (bytes_read < encoded_length);
|
||||||
|
|
||||||
|
wchar_t code_point;
|
||||||
|
auto read_bytes = mbrtowc(&code_point, bit_cast<char const*>(underlying.data()), encoded_length, nullptr);
|
||||||
|
VERIFY(read_bytes == encoded_length);
|
||||||
|
return code_point;
|
||||||
|
}
|
||||||
|
|
||||||
|
wint_t getwc(FILE* stream)
|
||||||
|
{
|
||||||
|
return fgetwc(stream);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue