Serenity Operating System
1/*
2 * Copyright (c) 2021, the SerenityOS developers.
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#pragma once
8
9#include <AK/Array.h>
10#include <AK/IntrusiveList.h>
11#include <AK/Types.h>
12#include <bits/FILE.h>
13#include <bits/pthread_integration.h>
14#include <bits/wchar.h>
15#include <pthread.h>
16#include <sys/types.h>
17
18struct FILE {
19public:
20 FILE(int fd, int mode)
21 : m_fd(fd)
22 , m_mode(mode)
23 {
24 pthread_mutexattr_t attr = { __PTHREAD_MUTEX_RECURSIVE };
25 pthread_mutex_init(&m_mutex, &attr);
26 }
27 ~FILE();
28
29 static FILE* create(int fd, int mode);
30
31 void setbuf(u8* data, int mode, size_t size) { m_buffer.setbuf(data, mode, size); }
32
33 bool flush();
34 void purge();
35 size_t pending();
36 bool close();
37
38 void lock();
39 void unlock();
40
41 int fileno() const { return m_fd; }
42 bool eof() const { return m_eof; }
43 int mode() const { return m_mode; }
44 u8 flags() const { return m_flags; }
45
46 int error() const { return m_error; }
47 void clear_err() { m_error = 0; }
48 void set_err() { m_error = 1; }
49
50 size_t read(u8*, size_t);
51 size_t write(u8 const*, size_t);
52
53 template<typename CharType>
54 bool gets(CharType*, size_t);
55
56 bool ungetc(u8 byte) { return m_buffer.enqueue_front(byte); }
57
58 int seek(off_t offset, int whence);
59 off_t tell();
60
61 pid_t popen_child() { return m_popen_child; }
62 void set_popen_child(pid_t child_pid) { m_popen_child = child_pid; }
63
64 void reopen(int fd, int mode);
65
66 u8 const* readptr(size_t& available_size);
67 void readptr_increase(size_t increment);
68
69 enum Flags : u8 {
70 None = 0,
71 LastRead = 1,
72 LastWrite = 2,
73 };
74
75private:
76 struct Buffer {
77 // A ringbuffer that also transparently implements ungetc().
78 public:
79 ~Buffer();
80
81 int mode() const { return m_mode; }
82 void setbuf(u8* data, int mode, size_t size);
83 // Make sure to call realize() before enqueuing any data.
84 // Dequeuing can be attempted without it.
85 void realize(int fd);
86 void drop();
87
88 bool may_use() const;
89 bool is_not_empty() const { return m_ungotten || !m_empty; }
90 size_t buffered_size() const;
91
92 u8 const* begin_dequeue(size_t& available_size) const;
93 void did_dequeue(size_t actual_size);
94
95 u8* begin_enqueue(size_t& available_size) const;
96 void did_enqueue(size_t actual_size);
97
98 bool enqueue_front(u8 byte);
99
100 private:
101 constexpr static auto unget_buffer_size = MB_CUR_MAX;
102 constexpr static u32 ungotten_mask = ((u32)0xffffffff) >> (sizeof(u32) * 8 - unget_buffer_size);
103
104 // Note: the fields here are arranged this way
105 // to make sizeof(Buffer) smaller.
106 u8* m_data { nullptr };
107 size_t m_capacity { BUFSIZ };
108 size_t m_begin { 0 };
109 size_t m_end { 0 };
110
111 int m_mode { -1 };
112 Array<u8, unget_buffer_size> m_unget_buffer { 0 };
113 u32 m_ungotten : unget_buffer_size { 0 };
114 bool m_data_is_malloced : 1 { false };
115 // When m_begin == m_end, we want to distinguish whether
116 // the buffer is full or empty.
117 bool m_empty : 1 { true };
118 };
119
120 // Read or write using the underlying fd, bypassing the buffer.
121 ssize_t do_read(u8*, size_t);
122 ssize_t do_write(u8 const*, size_t);
123
124 // Read some data into the buffer.
125 bool read_into_buffer();
126 // Flush *some* data from the buffer.
127 bool write_from_buffer();
128
129 int m_fd { -1 };
130 int m_mode { 0 };
131 u8 m_flags { Flags::None };
132 int m_error { 0 };
133 bool m_eof { false };
134 pid_t m_popen_child { -1 };
135 Buffer m_buffer;
136 __pthread_mutex_t m_mutex;
137 IntrusiveListNode<FILE> m_list_node;
138
139public:
140 using List = IntrusiveList<&FILE::m_list_node>;
141};
142
143class ScopedFileLock {
144public:
145 ScopedFileLock(FILE* file)
146 : m_file(file)
147 {
148 m_file->lock();
149 }
150
151 ~ScopedFileLock()
152 {
153 m_file->unlock();
154 }
155
156private:
157 FILE* m_file;
158};