mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 06:57:46 +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
|
||||
wchar.cpp
|
||||
wctype.cpp
|
||||
wstdio.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/StdLibExtras.h>
|
||||
#include <AK/String.h>
|
||||
#include <LibC/bits/pthread_integration.h>
|
||||
#include <LibC/bits/stdio_file_implementation.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
|
@ -25,118 +25,6 @@
|
|||
#include <syscall.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()
|
||||
{
|
||||
bool already_closed = m_fd == -1;
|
||||
|
@ -595,23 +483,6 @@ void FILE::unlock()
|
|||
__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" {
|
||||
|
||||
alignas(FILE) static u8 default_streams[3][sizeof(FILE)];
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <bits/FILE.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.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 wcsspn(const wchar_t* wcs, const wchar_t* accept);
|
||||
|
||||
wint_t fgetwc(FILE* stream);
|
||||
wint_t getwc(FILE* stream);
|
||||
|
||||
__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