1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
|
#include <io.h>
#include <libc.h>
#include <atomic.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
char __stdout_buffer[BUFSIZ];
void (*__stdio_cleanup)(void);
size_t fwrite(const void *restrict ptr, size_t size, size_t nitems,
FILE *restrict stream)
{
size_t total_bytes;
size_t written = 0;
const char *data = (const char *)ptr;
// Validate parameters
if (!ptr || !stream || size == 0) {
if (size == 0)
return nitems;
errno = EINVAL;
return 0;
}
// Special case for string buffer operations (snprintf)
// When fd is -1, we're writing to a string buffer
if (stream->fd == -1 && stream->buf != NULL) {
// Calculate total bytes with overflow check
if (__builtin_mul_overflow(size, nitems, &total_bytes)) {
errno = EOVERFLOW;
return 0;
}
// Check remaining buffer space (leave room for null terminator)
size_t space_left =
stream->buf_size > stream->buf_len ?
stream->buf_size - stream->buf_len - 1 :
0;
if (space_left == 0) {
return 0;
}
// Copy as much as fits
size_t to_copy = total_bytes < space_left ? total_bytes :
space_left;
if (to_copy > 0) {
memcpy(stream->buf + stream->buf_len, data, to_copy);
stream->buf_len += to_copy;
}
// Return number of complete items written
return to_copy == total_bytes ? nitems : to_copy / size;
}
// Calculate total bytes with overflow check
if (__builtin_mul_overflow(size, nitems, &total_bytes)) {
errno = EOVERFLOW;
return 0;
}
// Check if stream is writable (fix the flag check)
if ((stream->flags & O_ACCMODE) == O_RDONLY) {
errno = EBADF;
return 0;
}
// Check for error state
if (stream->flags & _IO_ERR) {
errno = EIO;
return 0;
}
LIBC_LOCK(stream->lock);
// Handle unbuffered I/O
if (stream->type == _IONBF) {
ssize_t result = write(stream->fd, data, total_bytes);
LIBC_UNLOCK(stream->lock);
if (result < 0) {
stream->flags |= _IO_ERR;
return 0;
}
return result == (ssize_t)total_bytes ? nitems : result / size;
}
// Handle buffered I/O (both _IOFBF and _IOLBF)
size_t remaining = total_bytes;
while (remaining > 0) {
// Check if buffer is full
size_t space_available = stream->buf_size - stream->buf_len;
if (space_available == 0) {
// Flush the buffer
LIBC_UNLOCK(stream->lock);
if (fflush(stream) != 0) {
return written / size;
}
space_available = stream->buf_size;
}
// Determine how much to copy this iteration
size_t to_copy = remaining < space_available ? remaining :
space_available;
// Copy data to buffer
memcpy(stream->buf + stream->buf_len, data + written, to_copy);
stream->buf_len += to_copy;
written += to_copy;
remaining -= to_copy;
// For line buffered streams, check if we need to flush
if (stream->type == _IOLBF) {
// Look for newlines in the data we just added
char *newline_pos =
memchr(stream->buf + stream->buf_len - to_copy,
'\n', to_copy);
if (newline_pos != NULL) {
LIBC_UNLOCK(stream->lock);
if (fflush(stream) != 0) {
return written / size;
}
}
}
// For fully buffered streams, flush if buffer is full
else if (stream->type == _IOFBF &&
stream->buf_len == stream->buf_size) {
LIBC_UNLOCK(stream->lock);
if (fflush(stream) != 0) {
return written / size;
}
}
}
LIBC_UNLOCK(stream->lock);
// Return number of complete items written
return written == total_bytes ? nitems : written / size;
}
|