Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/bc/src/read.c
39507 views
1
/*
2
* *****************************************************************************
3
*
4
* SPDX-License-Identifier: BSD-2-Clause
5
*
6
* Copyright (c) 2018-2025 Gavin D. Howard and contributors.
7
*
8
* Redistribution and use in source and binary forms, with or without
9
* modification, are permitted provided that the following conditions are met:
10
*
11
* * Redistributions of source code must retain the above copyright notice, this
12
* list of conditions and the following disclaimer.
13
*
14
* * Redistributions in binary form must reproduce the above copyright notice,
15
* this list of conditions and the following disclaimer in the documentation
16
* and/or other materials provided with the distribution.
17
*
18
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28
* POSSIBILITY OF SUCH DAMAGE.
29
*
30
* *****************************************************************************
31
*
32
* Code to handle special I/O for bc.
33
*
34
*/
35
36
#include <assert.h>
37
#include <ctype.h>
38
#include <errno.h>
39
#include <stdlib.h>
40
#include <string.h>
41
42
#include <signal.h>
43
44
#include <fcntl.h>
45
#include <sys/stat.h>
46
47
#ifndef _WIN32
48
#include <unistd.h>
49
#endif // _WIN32
50
51
#include <read.h>
52
#include <history.h>
53
#include <program.h>
54
#include <vm.h>
55
56
/**
57
* A portability file open function. This is copied to gen/strgen.c. Make sure
58
* to update that if this changes.
59
* @param path The path to the file to open.
60
* @param mode The mode to open in.
61
*/
62
static int
63
bc_read_open(const char* path, int mode)
64
{
65
int fd;
66
67
#ifndef _WIN32
68
fd = open(path, mode);
69
#else // _WIN32
70
fd = -1;
71
open(&fd, path, mode);
72
#endif
73
74
return fd;
75
}
76
77
/**
78
* Returns true if the buffer data is non-text.
79
* @param buf The buffer to test.
80
* @param size The size of the buffer.
81
*/
82
static bool
83
bc_read_binary(const char* buf, size_t size)
84
{
85
size_t i;
86
87
for (i = 0; i < size; ++i)
88
{
89
if (BC_ERR(BC_READ_BIN_CHAR(buf[i]))) return true;
90
}
91
92
return false;
93
}
94
95
bool
96
bc_read_buf(BcVec* vec, char* buf, size_t* buf_len)
97
{
98
char* nl;
99
100
// If nothing there, return.
101
if (!*buf_len) return false;
102
103
// Find the newline.
104
nl = strchr(buf, '\n');
105
106
// If a newline exists...
107
if (nl != NULL)
108
{
109
// Get the size of the data up to, and including, the newline.
110
size_t nllen = (size_t) ((nl + 1) - buf);
111
112
nllen = *buf_len >= nllen ? nllen : *buf_len;
113
114
// Move data into the vector, and move the rest of the data in the
115
// buffer up.
116
bc_vec_npush(vec, nllen, buf);
117
*buf_len -= nllen;
118
// NOLINTNEXTLINE
119
memmove(buf, nl + 1, *buf_len + 1);
120
121
return true;
122
}
123
124
// Just put the data into the vector.
125
bc_vec_npush(vec, *buf_len, buf);
126
*buf_len = 0;
127
128
return false;
129
}
130
131
BcStatus
132
bc_read_chars(BcVec* vec, const char* prompt)
133
{
134
bool done = false;
135
136
assert(vec != NULL && vec->size == sizeof(char));
137
138
BC_SIG_ASSERT_NOT_LOCKED;
139
140
// Clear the vector.
141
bc_vec_popAll(vec);
142
143
// Handle the prompt, if desired.
144
if (BC_PROMPT)
145
{
146
bc_file_puts(&vm->fout, bc_flush_none, prompt);
147
bc_file_flush(&vm->fout, bc_flush_none);
148
}
149
150
// Try reading from the buffer, and if successful, just return.
151
if (bc_read_buf(vec, vm->buf, &vm->buf_len))
152
{
153
bc_vec_pushByte(vec, '\0');
154
return BC_STATUS_SUCCESS;
155
}
156
157
// Loop until we have something.
158
while (!done)
159
{
160
ssize_t r;
161
162
BC_SIG_LOCK;
163
164
// Read data from stdin.
165
r = read(STDIN_FILENO, vm->buf + vm->buf_len,
166
BC_VM_STDIN_BUF_SIZE - vm->buf_len);
167
168
// If there was an error...
169
if (BC_UNLIKELY(r < 0))
170
{
171
// If interupted...
172
if (errno == EINTR)
173
{
174
// Jump out if we are supposed to quit, which certain signals
175
// will require.
176
if (vm->status == (sig_atomic_t) BC_STATUS_QUIT) BC_JMP;
177
178
assert(vm->sig != 0);
179
180
// Clear the signal and status.
181
vm->sig = 0;
182
vm->status = (sig_atomic_t) BC_STATUS_SUCCESS;
183
184
// Print the ready message and prompt again.
185
bc_file_puts(&vm->fout, bc_flush_none, bc_program_ready_msg);
186
if (BC_PROMPT)
187
{
188
bc_file_puts(&vm->fout, bc_flush_none, prompt);
189
}
190
bc_file_flush(&vm->fout, bc_flush_none);
191
192
BC_SIG_UNLOCK;
193
194
continue;
195
}
196
197
BC_SIG_UNLOCK;
198
199
// If we get here, it's bad. Barf.
200
bc_vm_fatalError(BC_ERR_FATAL_IO_ERR);
201
}
202
203
BC_SIG_UNLOCK;
204
205
// If we read nothing, make sure to terminate the string and return EOF.
206
if (r == 0)
207
{
208
bc_vec_pushByte(vec, '\0');
209
return BC_STATUS_EOF;
210
}
211
212
BC_SIG_LOCK;
213
214
// Add to the buffer.
215
vm->buf_len += (size_t) r;
216
vm->buf[vm->buf_len] = '\0';
217
218
// Read from the buffer.
219
done = bc_read_buf(vec, vm->buf, &vm->buf_len);
220
221
BC_SIG_UNLOCK;
222
}
223
224
// Terminate the string.
225
bc_vec_pushByte(vec, '\0');
226
227
return BC_STATUS_SUCCESS;
228
}
229
230
BcStatus
231
bc_read_line(BcVec* vec, const char* prompt)
232
{
233
BcStatus s;
234
235
#if BC_ENABLE_HISTORY
236
// Get a line from either history or manual reading.
237
if (BC_TTY && !vm->history.badTerm)
238
{
239
s = bc_history_line(&vm->history, vec, prompt);
240
}
241
else s = bc_read_chars(vec, prompt);
242
#else // BC_ENABLE_HISTORY
243
s = bc_read_chars(vec, prompt);
244
#endif // BC_ENABLE_HISTORY
245
246
if (BC_ERR(bc_read_binary(vec->v, vec->len - 1)))
247
{
248
bc_verr(BC_ERR_FATAL_BIN_FILE, bc_program_stdin_name);
249
}
250
251
return s;
252
}
253
254
char*
255
bc_read_file(const char* path)
256
{
257
BcErr e = BC_ERR_FATAL_IO_ERR;
258
size_t size, to_read;
259
struct stat pstat;
260
int fd;
261
char* buf;
262
char* buf2;
263
264
// This has been copied to gen/strgen.c. Make sure to change that if this
265
// changes.
266
267
BC_SIG_ASSERT_LOCKED;
268
269
assert(path != NULL);
270
271
#if BC_DEBUG
272
// Need this to quiet MSan.
273
// NOLINTNEXTLINE
274
memset(&pstat, 0, sizeof(struct stat));
275
#endif // BC_DEBUG
276
277
fd = bc_read_open(path, O_RDONLY);
278
279
// If we can't read a file, we just barf.
280
if (BC_ERR(fd < 0)) bc_verr(BC_ERR_FATAL_FILE_ERR, path);
281
282
// The reason we call fstat is to eliminate TOCTOU race conditions. This
283
// way, we have an open file, so it's not going anywhere.
284
if (BC_ERR(fstat(fd, &pstat) == -1)) goto malloc_err;
285
286
// Make sure it's not a directory.
287
if (BC_ERR(S_ISDIR(pstat.st_mode)))
288
{
289
e = BC_ERR_FATAL_PATH_DIR;
290
goto malloc_err;
291
}
292
293
// Get the size of the file and allocate that much.
294
size = (size_t) pstat.st_size;
295
buf = bc_vm_malloc(size + 1);
296
buf2 = buf;
297
to_read = size;
298
299
do
300
{
301
// Read the file. We just bail if a signal interrupts. This is so that
302
// users can interrupt the reading of big files if they want.
303
ssize_t r = read(fd, buf2, to_read);
304
if (BC_ERR(r < 0)) goto read_err;
305
to_read -= (size_t) r;
306
buf2 += (size_t) r;
307
}
308
while (to_read);
309
310
// Got to have a nul byte.
311
buf[size] = '\0';
312
313
if (BC_ERR(bc_read_binary(buf, size)))
314
{
315
e = BC_ERR_FATAL_BIN_FILE;
316
goto read_err;
317
}
318
319
close(fd);
320
321
return buf;
322
323
read_err:
324
free(buf);
325
malloc_err:
326
close(fd);
327
bc_verr(e, path);
328
return NULL;
329
}
330
331