2026-04-02 16:00:27 +02:00
|
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
2026-04-02 16:25:49 +02:00
|
|
|
// Copyright (C) 2011 Andrea Mazzoleni
|
2015-02-21 09:00:34 +01:00
|
|
|
|
2026-06-24 00:15:15 +02:00
|
|
|
#include "os/portable.h"
|
2015-02-21 09:00:34 +01:00
|
|
|
|
|
|
|
|
#include "support.h"
|
|
|
|
|
#include "util.h"
|
|
|
|
|
#include "stream.h"
|
|
|
|
|
|
|
|
|
|
/****************************************************************************/
|
|
|
|
|
/* stream */
|
|
|
|
|
|
2016-01-16 07:27:28 +01:00
|
|
|
unsigned STREAM_SIZE = 64 * 1024;
|
2015-02-21 09:00:34 +01:00
|
|
|
|
2026-01-10 15:30:25 +01:00
|
|
|
STREAM* sopen_read(const char* file, int flags)
|
2015-02-21 09:00:34 +01:00
|
|
|
{
|
|
|
|
|
STREAM* s = malloc_nofail(sizeof(STREAM));
|
2026-01-10 15:30:25 +01:00
|
|
|
struct stat st;
|
2026-09-09 20:39:33 +02:00
|
|
|
int ret;
|
2015-02-21 09:00:34 +01:00
|
|
|
|
|
|
|
|
s->handle_size = 1;
|
|
|
|
|
s->handle = malloc_nofail(sizeof(struct stream_handle));
|
2026-09-09 20:39:33 +02:00
|
|
|
s->advise = malloc_nofail(sizeof(struct advise_struct));
|
2026-01-10 15:30:25 +01:00
|
|
|
s->flags = flags;
|
2026-09-09 20:39:33 +02:00
|
|
|
advise_init(&s->advise[0], s->flags & STREAM_FLAGS_ADVISE_MASK);
|
2026-01-10 15:30:25 +01:00
|
|
|
|
2026-09-09 20:39:33 +02:00
|
|
|
int open_flags = O_RDONLY | O_BINARY | advise_flags(&s->advise[0]);
|
2026-09-25 09:21:06 +02:00
|
|
|
if ((s->flags & STREAM_FLAGS_NOFOLLOW) != 0)
|
|
|
|
|
open_flags |= O_NOFOLLOW;
|
2015-02-21 09:00:34 +01:00
|
|
|
|
|
|
|
|
pathcpy(s->handle[0].path, sizeof(s->handle[0].path), file);
|
2026-01-10 15:30:25 +01:00
|
|
|
s->handle[0].f = open(file, open_flags);
|
2015-02-21 09:00:34 +01:00
|
|
|
if (s->handle[0].f == -1) {
|
2026-09-09 20:39:33 +02:00
|
|
|
free(s->advise);
|
2015-02-21 09:00:34 +01:00
|
|
|
free(s->handle);
|
|
|
|
|
free(s);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-10 15:30:25 +01:00
|
|
|
/* get file info */
|
|
|
|
|
if (fstat(s->handle[0].f, &st) != 0) {
|
2015-02-21 09:00:34 +01:00
|
|
|
close(s->handle[0].f);
|
2026-09-09 20:39:33 +02:00
|
|
|
free(s->advise);
|
2015-02-21 09:00:34 +01:00
|
|
|
free(s->handle);
|
|
|
|
|
free(s);
|
|
|
|
|
return 0;
|
2026-01-10 15:30:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
s->size = st.st_size;
|
2026-09-09 20:39:33 +02:00
|
|
|
ret = advise_open(&s->advise[0], s->handle[0].f);
|
|
|
|
|
if (ret != 0) {
|
|
|
|
|
/* LCOV_EXCL_START */
|
|
|
|
|
close(s->handle[0].f);
|
|
|
|
|
free(s->advise);
|
|
|
|
|
free(s->handle);
|
|
|
|
|
free(s);
|
|
|
|
|
return 0;
|
|
|
|
|
/* LCOV_EXCL_STOP */
|
2015-02-21 09:00:34 +01:00
|
|
|
}
|
|
|
|
|
|
2026-01-10 15:30:25 +01:00
|
|
|
s->buffer_size = STREAM_SIZE;
|
|
|
|
|
if (s->buffer_size > s->size)
|
|
|
|
|
s->buffer_size = s->size; /* allocate only what is necessary */
|
2026-09-10 08:41:14 +02:00
|
|
|
s->buffer = malloc_nofail(s->buffer_size);
|
|
|
|
|
mtest_vector(1, s->buffer_size, (void**)&s->buffer);
|
2015-02-21 09:00:34 +01:00
|
|
|
s->pos = s->buffer;
|
|
|
|
|
s->end = s->buffer;
|
|
|
|
|
s->state = STREAM_STATE_READ;
|
|
|
|
|
s->state_index = 0;
|
|
|
|
|
s->offset = 0;
|
|
|
|
|
s->offset_uncached = 0;
|
|
|
|
|
s->crc = 0;
|
|
|
|
|
s->crc_uncached = 0;
|
|
|
|
|
|
|
|
|
|
return s;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-10 15:30:25 +01:00
|
|
|
STREAM* sopen_multi_write(unsigned count, int flags)
|
2015-02-21 09:00:34 +01:00
|
|
|
{
|
|
|
|
|
unsigned i;
|
|
|
|
|
|
|
|
|
|
STREAM* s = malloc_nofail(sizeof(STREAM));
|
|
|
|
|
|
|
|
|
|
s->handle_size = count;
|
2026-07-06 18:38:50 +02:00
|
|
|
s->handle = nalloc_nofail(count, sizeof(struct stream_handle));
|
2026-09-09 20:39:33 +02:00
|
|
|
s->advise = nalloc_nofail(count, sizeof(struct advise_struct));
|
2026-01-10 15:30:25 +01:00
|
|
|
s->flags = flags;
|
2015-02-21 09:00:34 +01:00
|
|
|
|
2026-09-09 20:39:33 +02:00
|
|
|
for (i = 0; i < count; ++i) {
|
2015-02-21 09:00:34 +01:00
|
|
|
s->handle[i].f = -1;
|
2026-09-09 20:39:33 +02:00
|
|
|
advise_init(&s->advise[i], s->flags & STREAM_FLAGS_ADVISE_MASK);
|
|
|
|
|
}
|
2015-02-21 09:00:34 +01:00
|
|
|
|
2026-01-10 15:30:25 +01:00
|
|
|
s->buffer_size = STREAM_SIZE;
|
2026-09-10 08:41:14 +02:00
|
|
|
s->buffer = malloc_nofail(s->buffer_size);
|
|
|
|
|
mtest_vector(1, s->buffer_size, (void**)&s->buffer);
|
2015-02-21 09:00:34 +01:00
|
|
|
s->pos = s->buffer;
|
2026-01-10 15:30:25 +01:00
|
|
|
s->end = s->buffer + s->buffer_size;
|
2015-02-21 09:00:34 +01:00
|
|
|
s->state = STREAM_STATE_WRITE;
|
|
|
|
|
s->state_index = 0;
|
2026-01-10 15:30:25 +01:00
|
|
|
s->size = 0;
|
2015-02-21 09:00:34 +01:00
|
|
|
s->offset = 0;
|
|
|
|
|
s->offset_uncached = 0;
|
|
|
|
|
s->crc = 0;
|
|
|
|
|
s->crc_uncached = 0;
|
|
|
|
|
|
|
|
|
|
return s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int sopen_multi_file(STREAM* s, unsigned i, const char* file)
|
|
|
|
|
{
|
|
|
|
|
int f;
|
|
|
|
|
|
|
|
|
|
pathcpy(s->handle[i].path, sizeof(s->handle[i].path), file);
|
|
|
|
|
|
2026-09-09 20:39:33 +02:00
|
|
|
int open_flags = O_WRONLY | O_CREAT | O_EXCL | O_BINARY | advise_flags(&s->advise[i]);
|
2026-01-10 15:30:25 +01:00
|
|
|
|
2020-05-02 14:21:40 +02:00
|
|
|
/* O_EXCL to be resilient ensure to always create a new file and not use a stale link to the original file */
|
2026-01-10 15:30:25 +01:00
|
|
|
f = open(file, open_flags, 0600);
|
2015-02-21 09:00:34 +01:00
|
|
|
if (f == -1) {
|
|
|
|
|
/* LCOV_EXCL_START */
|
|
|
|
|
return -1;
|
|
|
|
|
/* LCOV_EXCL_STOP */
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-09 20:39:33 +02:00
|
|
|
{
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
ret = advise_open(&s->advise[i], f);
|
2026-01-10 15:30:25 +01:00
|
|
|
if (ret != 0) {
|
|
|
|
|
/* LCOV_EXCL_START */
|
|
|
|
|
close(f);
|
|
|
|
|
return -1;
|
|
|
|
|
/* LCOV_EXCL_STOP */
|
|
|
|
|
}
|
2015-02-21 09:00:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
s->handle[i].f = f;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-10 15:30:25 +01:00
|
|
|
STREAM* sopen_write(const char* file, int flags)
|
2015-10-29 23:37:58 +01:00
|
|
|
{
|
2026-01-10 15:30:25 +01:00
|
|
|
STREAM* s = sopen_multi_write(1, flags);
|
2015-10-29 23:37:58 +01:00
|
|
|
|
|
|
|
|
if (sopen_multi_file(s, 0, file) != 0) {
|
|
|
|
|
sclose(s);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return s;
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-21 09:00:34 +01:00
|
|
|
int sclose(STREAM* s)
|
|
|
|
|
{
|
|
|
|
|
int fail = 0;
|
|
|
|
|
unsigned i;
|
|
|
|
|
|
|
|
|
|
if (s->state == STREAM_STATE_WRITE) {
|
|
|
|
|
if (sflush(s) != 0) {
|
|
|
|
|
/* LCOV_EXCL_START */
|
|
|
|
|
fail = 1;
|
|
|
|
|
/* LCOV_EXCL_STOP */
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < s->handle_size; ++i) {
|
2026-05-21 16:27:00 +02:00
|
|
|
/* if open failed, there is nothing to close */
|
|
|
|
|
if (s->handle[i].f != -1) {
|
2026-09-09 20:39:33 +02:00
|
|
|
if (advise_close(&s->advise[i], s->handle[i].f) != 0) {
|
|
|
|
|
/* LCOV_EXCL_START */
|
|
|
|
|
fail = 1;
|
|
|
|
|
/* LCOV_EXCL_STOP */
|
|
|
|
|
}
|
2026-05-21 16:27:00 +02:00
|
|
|
if (close(s->handle[i].f) != 0) {
|
|
|
|
|
/* LCOV_EXCL_START */
|
|
|
|
|
fail = 1;
|
|
|
|
|
/* LCOV_EXCL_STOP */
|
|
|
|
|
}
|
2015-02-21 09:00:34 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-09 20:39:33 +02:00
|
|
|
free(s->advise);
|
2015-02-21 09:00:34 +01:00
|
|
|
free(s->handle);
|
|
|
|
|
free(s->buffer);
|
|
|
|
|
free(s);
|
|
|
|
|
|
|
|
|
|
if (fail) {
|
|
|
|
|
/* LCOV_EXCL_START */
|
|
|
|
|
return -1;
|
|
|
|
|
/* LCOV_EXCL_STOP */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int shandle(STREAM* s)
|
|
|
|
|
{
|
|
|
|
|
if (!s->handle_size) {
|
|
|
|
|
/* LCOV_EXCL_START */
|
|
|
|
|
return -1;
|
|
|
|
|
/* LCOV_EXCL_STOP */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return s->handle[0].f;
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-28 19:36:24 +01:00
|
|
|
/**
|
|
|
|
|
* Fill the read stream buffer.
|
|
|
|
|
* \return 0 if at least on char is read, or EOF on error.
|
|
|
|
|
*/
|
|
|
|
|
static int sfill(STREAM* s)
|
2015-02-21 09:00:34 +01:00
|
|
|
{
|
|
|
|
|
ssize_t ret;
|
|
|
|
|
|
|
|
|
|
if (s->state != STREAM_STATE_READ) {
|
|
|
|
|
/* LCOV_EXCL_START */
|
|
|
|
|
return EOF;
|
|
|
|
|
/* LCOV_EXCL_STOP */
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-17 17:26:49 +02:00
|
|
|
/* expose the logical size boundary as EOF, just like EOF from the underlying file */
|
2026-01-10 15:30:25 +01:00
|
|
|
if (s->offset >= s->size) {
|
2026-08-17 17:26:49 +02:00
|
|
|
s->state = STREAM_STATE_EOF;
|
2026-01-10 15:30:25 +01:00
|
|
|
return EOF;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ret = read(s->handle[0].f, s->buffer, s->buffer_size);
|
2015-02-21 09:00:34 +01:00
|
|
|
|
|
|
|
|
if (ret < 0) {
|
|
|
|
|
/* LCOV_EXCL_START */
|
|
|
|
|
s->state = STREAM_STATE_ERROR;
|
|
|
|
|
return EOF;
|
|
|
|
|
/* LCOV_EXCL_STOP */
|
|
|
|
|
}
|
|
|
|
|
if (ret == 0) {
|
2026-01-10 15:30:25 +01:00
|
|
|
/* nothing mode to read */
|
2015-02-21 09:00:34 +01:00
|
|
|
s->state = STREAM_STATE_EOF;
|
|
|
|
|
return EOF;
|
|
|
|
|
}
|
2026-01-10 15:30:25 +01:00
|
|
|
/* assume that it can be a shorter read, so ret < s->buffer_size doesn't mean that we reached EOF */
|
2015-02-21 09:00:34 +01:00
|
|
|
|
2026-09-09 20:39:33 +02:00
|
|
|
if (advise_read(&s->advise[0], s->handle[0].f, s->offset, ret) != 0) {
|
|
|
|
|
/* LCOV_EXCL_START */
|
|
|
|
|
s->state = STREAM_STATE_ERROR;
|
|
|
|
|
return EOF;
|
|
|
|
|
/* LCOV_EXCL_STOP */
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-21 09:00:34 +01:00
|
|
|
/* update the crc */
|
2026-01-10 15:30:25 +01:00
|
|
|
if (s->flags & STREAM_FLAGS_CRC) {
|
|
|
|
|
s->crc_uncached = s->crc;
|
|
|
|
|
s->crc = crc32c(s->crc, s->buffer, ret);
|
|
|
|
|
}
|
2015-02-21 09:00:34 +01:00
|
|
|
|
|
|
|
|
/* update the offset */
|
|
|
|
|
s->offset_uncached = s->offset;
|
|
|
|
|
s->offset += ret;
|
|
|
|
|
|
|
|
|
|
s->pos = s->buffer;
|
|
|
|
|
s->end = s->buffer + ret;
|
|
|
|
|
|
2015-10-28 19:36:24 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int sdeplete(STREAM* s, unsigned char* last)
|
|
|
|
|
{
|
|
|
|
|
/* last four bytes */
|
|
|
|
|
last[0] = 0;
|
|
|
|
|
last[1] = 0;
|
|
|
|
|
last[2] = 0;
|
|
|
|
|
last[3] = 0;
|
|
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
|
/* increase the position up to 4 bytes before the end */
|
2026-08-17 10:12:03 +02:00
|
|
|
if (s->end - s->pos >= 4)
|
2015-10-28 19:36:24 +01:00
|
|
|
s->pos = s->end - 4;
|
|
|
|
|
|
|
|
|
|
/* insert the last 4 bytes */
|
|
|
|
|
while (s->pos < s->end) {
|
|
|
|
|
last[0] = last[1];
|
|
|
|
|
last[1] = last[2];
|
|
|
|
|
last[2] = last[3];
|
|
|
|
|
last[3] = *s->pos++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* fill again the buffer until the end of the file */
|
|
|
|
|
if (sfill(s) != 0) {
|
|
|
|
|
/* on error fail */
|
|
|
|
|
if (serror(s)) {
|
|
|
|
|
/* LCOV_EXCL_START */
|
|
|
|
|
return EOF;
|
|
|
|
|
/* LCOV_EXCL_STOP */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* on EOF terminate */
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
2015-02-21 09:00:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int sflush(STREAM* s)
|
|
|
|
|
{
|
|
|
|
|
ssize_t ret;
|
|
|
|
|
ssize_t size;
|
2026-08-16 16:09:12 +02:00
|
|
|
ssize_t count;
|
2015-02-21 09:00:34 +01:00
|
|
|
unsigned i;
|
|
|
|
|
|
|
|
|
|
if (s->state != STREAM_STATE_WRITE) {
|
|
|
|
|
/* LCOV_EXCL_START */
|
|
|
|
|
return EOF;
|
|
|
|
|
/* LCOV_EXCL_STOP */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size = s->pos - s->buffer;
|
|
|
|
|
if (!size)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < s->handle_size; ++i) {
|
2026-08-16 16:09:12 +02:00
|
|
|
count = 0;
|
|
|
|
|
do {
|
|
|
|
|
ret = write(s->handle[i].f, s->buffer + count, size - count);
|
2015-02-21 09:00:34 +01:00
|
|
|
|
2026-08-16 16:09:12 +02:00
|
|
|
if (ret < 0) {
|
|
|
|
|
if (errno == EINTR)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
/* LCOV_EXCL_START */
|
|
|
|
|
s->state = STREAM_STATE_ERROR;
|
|
|
|
|
s->state_index = i;
|
|
|
|
|
return EOF;
|
|
|
|
|
/* LCOV_EXCL_STOP */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ret == 0) {
|
|
|
|
|
/* LCOV_EXCL_START */
|
|
|
|
|
errno = EIO;
|
|
|
|
|
s->state = STREAM_STATE_ERROR;
|
|
|
|
|
s->state_index = i;
|
|
|
|
|
return EOF;
|
|
|
|
|
/* LCOV_EXCL_STOP */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
count += ret;
|
|
|
|
|
} while (count < size);
|
2015-02-21 09:00:34 +01:00
|
|
|
}
|
|
|
|
|
|
2026-09-09 20:39:33 +02:00
|
|
|
for (i = 0; i < s->handle_size; ++i) {
|
|
|
|
|
/* ADVISE_DISCARD modes only discard ranges after waiting for writeback. */
|
|
|
|
|
if (advise_write(&s->advise[i], s->handle[i].f, s->offset, size) != 0) {
|
|
|
|
|
/* LCOV_EXCL_START */
|
|
|
|
|
s->state = STREAM_STATE_ERROR;
|
|
|
|
|
s->state_index = i;
|
|
|
|
|
return EOF;
|
|
|
|
|
/* LCOV_EXCL_STOP */
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-29 22:32:07 +01:00
|
|
|
/*
|
|
|
|
|
* Update the crc *after* writing the data.
|
|
|
|
|
*
|
|
|
|
|
* This must be done after the file write,
|
|
|
|
|
* to be able to detect memory errors on the buffer,
|
|
|
|
|
* happening during the write.
|
|
|
|
|
*/
|
2026-01-10 15:30:25 +01:00
|
|
|
if (s->flags & STREAM_FLAGS_CRC) {
|
|
|
|
|
s->crc = crc32c(s->crc, s->buffer, size);
|
|
|
|
|
s->crc_uncached = s->crc;
|
|
|
|
|
}
|
2015-10-29 22:32:07 +01:00
|
|
|
|
2015-02-21 09:00:34 +01:00
|
|
|
/* update the offset */
|
|
|
|
|
s->offset += size;
|
|
|
|
|
s->offset_uncached = s->offset;
|
|
|
|
|
|
|
|
|
|
s->pos = s->buffer;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int64_t stell(STREAM* s)
|
|
|
|
|
{
|
|
|
|
|
return s->offset_uncached + (s->pos - s->buffer);
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-10 15:30:25 +01:00
|
|
|
uint32_t scrc(STREAM* s)
|
2015-02-21 09:00:34 +01:00
|
|
|
{
|
2026-01-10 15:30:25 +01:00
|
|
|
assert(s->flags & STREAM_FLAGS_CRC);
|
2015-02-21 09:00:34 +01:00
|
|
|
return crc32c(s->crc_uncached, s->buffer, s->pos - s->buffer);
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-28 19:36:24 +01:00
|
|
|
int sgetc_uncached(STREAM* s)
|
|
|
|
|
{
|
|
|
|
|
/* if at the end of the buffer, fill it */
|
|
|
|
|
if (s->pos == s->end && sfill(s) != 0)
|
|
|
|
|
return EOF;
|
|
|
|
|
return *s->pos++;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-04 00:34:51 +02:00
|
|
|
ssize_t sgettok(STREAM* f, char* str, int size)
|
2015-02-21 09:00:34 +01:00
|
|
|
{
|
|
|
|
|
char* i = str;
|
|
|
|
|
char* send = str + size;
|
|
|
|
|
int c;
|
|
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
|
c = sgetc(f);
|
|
|
|
|
if (c == EOF) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (c == ' ' || c == '\t') {
|
|
|
|
|
sungetc(c, f);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (c == '\n') {
|
2020-05-02 14:21:40 +02:00
|
|
|
/* remove ending carriage return to support the Windows CR+LF format */
|
2015-02-21 09:00:34 +01:00
|
|
|
if (i != str && i[-1] == '\r')
|
|
|
|
|
--i;
|
|
|
|
|
sungetc(c, f);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*i++ = c;
|
|
|
|
|
|
|
|
|
|
if (i == send) {
|
|
|
|
|
/* LCOV_EXCL_START */
|
|
|
|
|
return -1;
|
|
|
|
|
/* LCOV_EXCL_STOP */
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*i = 0;
|
|
|
|
|
|
|
|
|
|
return i - str;
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-09 18:15:41 +02:00
|
|
|
int sread_uncached(STREAM* f, void* void_data, size_t size)
|
2015-02-21 09:00:34 +01:00
|
|
|
{
|
|
|
|
|
unsigned char* data = void_data;
|
|
|
|
|
|
2026-09-09 18:15:41 +02:00
|
|
|
/* standard version using sgetc() */
|
|
|
|
|
while (size--) {
|
|
|
|
|
int c = sgetc(f);
|
|
|
|
|
if (c == EOF) {
|
|
|
|
|
/* LCOV_EXCL_START */
|
|
|
|
|
return -1;
|
|
|
|
|
/* LCOV_EXCL_STOP */
|
2015-02-21 09:00:34 +01:00
|
|
|
}
|
2026-09-09 18:15:41 +02:00
|
|
|
|
|
|
|
|
*data++ = c;
|
2015-02-21 09:00:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-04 00:34:51 +02:00
|
|
|
ssize_t sgetline(STREAM* f, char* str, int size)
|
2015-02-21 09:00:34 +01:00
|
|
|
{
|
|
|
|
|
char* i = str;
|
|
|
|
|
char* send = str + size;
|
|
|
|
|
int c;
|
|
|
|
|
|
|
|
|
|
/* if there is enough data in memory */
|
|
|
|
|
if (sptrlookup(f, size)) {
|
|
|
|
|
/* optimized version with all the data in memory */
|
|
|
|
|
unsigned char* pos = sptrget(f);
|
|
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
|
c = *pos++;
|
|
|
|
|
if (c == '\n') {
|
2020-05-02 14:21:40 +02:00
|
|
|
/* remove ending carriage return to support the Windows CR+LF format */
|
2015-02-21 09:00:34 +01:00
|
|
|
if (i != str && i[-1] == '\r')
|
|
|
|
|
--i;
|
|
|
|
|
--pos;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-10 15:30:25 +01:00
|
|
|
if (i + 1 >= send) {
|
2015-02-21 09:00:34 +01:00
|
|
|
/* LCOV_EXCL_START */
|
2026-01-10 15:30:25 +01:00
|
|
|
*i = 0;
|
|
|
|
|
--pos;
|
|
|
|
|
sptrset(f, pos);
|
2015-02-21 09:00:34 +01:00
|
|
|
return -1;
|
|
|
|
|
/* LCOV_EXCL_STOP */
|
|
|
|
|
}
|
2026-01-10 15:30:25 +01:00
|
|
|
|
|
|
|
|
*i++ = c;
|
2015-02-21 09:00:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sptrset(f, pos);
|
|
|
|
|
} else {
|
|
|
|
|
while (1) {
|
|
|
|
|
c = sgetc(f);
|
|
|
|
|
if (c == EOF) {
|
|
|
|
|
/* LCOV_EXCL_START */
|
|
|
|
|
break;
|
|
|
|
|
/* LCOV_EXCL_STOP */
|
|
|
|
|
}
|
2026-01-10 15:30:25 +01:00
|
|
|
|
2015-02-21 09:00:34 +01:00
|
|
|
if (c == '\n') {
|
2020-05-02 14:21:40 +02:00
|
|
|
/* remove ending carriage return to support the Windows CR+LF format */
|
2015-02-21 09:00:34 +01:00
|
|
|
if (i != str && i[-1] == '\r')
|
|
|
|
|
--i;
|
|
|
|
|
sungetc(c, f);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-10 15:30:25 +01:00
|
|
|
if (i + 1 >= send) {
|
2015-02-21 09:00:34 +01:00
|
|
|
/* LCOV_EXCL_START */
|
2026-01-10 15:30:25 +01:00
|
|
|
*i = 0;
|
|
|
|
|
sungetc(c, f);
|
2015-02-21 09:00:34 +01:00
|
|
|
return -1;
|
|
|
|
|
/* LCOV_EXCL_STOP */
|
|
|
|
|
}
|
2026-01-10 15:30:25 +01:00
|
|
|
|
|
|
|
|
*i++ = c;
|
2015-02-21 09:00:34 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*i = 0;
|
|
|
|
|
|
|
|
|
|
return i - str;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-04 00:34:51 +02:00
|
|
|
ssize_t sgetlasttok(STREAM* f, char* str, int size)
|
2015-02-21 09:00:34 +01:00
|
|
|
{
|
2026-06-04 00:34:51 +02:00
|
|
|
ssize_t ret;
|
2015-02-21 09:00:34 +01:00
|
|
|
|
|
|
|
|
ret = sgetline(f, str, size);
|
|
|
|
|
if (ret < 0) {
|
|
|
|
|
/* LCOV_EXCL_START */
|
|
|
|
|
return ret;
|
|
|
|
|
/* LCOV_EXCL_STOP */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (ret > 0 && (str[ret - 1] == ' ' || str[ret - 1] == '\t'))
|
|
|
|
|
--ret;
|
|
|
|
|
|
|
|
|
|
str[ret] = 0;
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int sgetu32(STREAM* f, uint32_t* value)
|
|
|
|
|
{
|
|
|
|
|
int c;
|
|
|
|
|
|
|
|
|
|
c = sgetc(f);
|
2021-05-24 19:43:53 +02:00
|
|
|
if (c == '0') {
|
|
|
|
|
*value = 0;
|
|
|
|
|
return 0;
|
|
|
|
|
} else if (c >= '1' && c <= '9') {
|
2015-02-21 09:00:34 +01:00
|
|
|
uint32_t v;
|
|
|
|
|
|
|
|
|
|
v = c - '0';
|
|
|
|
|
|
|
|
|
|
c = sgetc(f);
|
|
|
|
|
while (c >= '0' && c <= '9') {
|
2021-05-24 19:43:53 +02:00
|
|
|
uint32_t digit;
|
|
|
|
|
if (v > 0xFFFFFFFFU / 10) {
|
|
|
|
|
/* LCOV_EXCL_START */
|
|
|
|
|
/* overflow */
|
|
|
|
|
return -1;
|
|
|
|
|
/* LCOV_EXCL_STOP */
|
|
|
|
|
}
|
2015-02-21 09:00:34 +01:00
|
|
|
v *= 10;
|
2021-05-24 19:43:53 +02:00
|
|
|
|
|
|
|
|
digit = c - '0';
|
|
|
|
|
if (v > 0xFFFFFFFFU - digit) {
|
|
|
|
|
/* LCOV_EXCL_START */
|
|
|
|
|
/* overflow */
|
|
|
|
|
return -1;
|
|
|
|
|
/* LCOV_EXCL_STOP */
|
|
|
|
|
}
|
|
|
|
|
v += digit;
|
|
|
|
|
|
2015-02-21 09:00:34 +01:00
|
|
|
c = sgetc(f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*value = v;
|
|
|
|
|
|
|
|
|
|
sungetc(c, f);
|
|
|
|
|
return 0;
|
|
|
|
|
} else {
|
|
|
|
|
/* LCOV_EXCL_START */
|
|
|
|
|
/* nothing read */
|
|
|
|
|
return -1;
|
|
|
|
|
/* LCOV_EXCL_STOP */
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int sgetb32(STREAM* f, uint32_t* value)
|
|
|
|
|
{
|
|
|
|
|
uint32_t v;
|
|
|
|
|
unsigned char b;
|
2026-07-30 10:58:35 +02:00
|
|
|
unsigned s;
|
2015-02-21 09:00:34 +01:00
|
|
|
int c;
|
|
|
|
|
|
|
|
|
|
v = 0;
|
|
|
|
|
s = 0;
|
|
|
|
|
loop:
|
|
|
|
|
c = sgetc(f);
|
|
|
|
|
if (c == EOF) {
|
|
|
|
|
/* LCOV_EXCL_START */
|
|
|
|
|
return -1;
|
|
|
|
|
/* LCOV_EXCL_STOP */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
b = (unsigned char)c;
|
|
|
|
|
if ((b & 0x80) == 0) {
|
|
|
|
|
v |= (uint32_t)b << s;
|
|
|
|
|
s += 7;
|
2026-07-30 10:58:35 +02:00
|
|
|
if (tommy_unlikely(s >= 32)) {
|
2015-02-21 09:00:34 +01:00
|
|
|
/* LCOV_EXCL_START */
|
|
|
|
|
return -1;
|
|
|
|
|
/* LCOV_EXCL_STOP */
|
|
|
|
|
}
|
|
|
|
|
goto loop;
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-30 10:58:35 +02:00
|
|
|
/*
|
|
|
|
|
* Check for overflow: at s == 28 only 4 bits (0x0f) fit in uint32_t
|
|
|
|
|
*/
|
|
|
|
|
if (tommy_unlikely(s == 28 && (b & 0x70) != 0)) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-21 09:00:34 +01:00
|
|
|
v |= (uint32_t)(b & 0x7f) << s;
|
|
|
|
|
|
|
|
|
|
*value = v;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int sgetb64(STREAM* f, uint64_t* value)
|
|
|
|
|
{
|
|
|
|
|
uint64_t v;
|
|
|
|
|
unsigned char b;
|
2026-07-30 10:58:35 +02:00
|
|
|
unsigned s;
|
2015-02-21 09:00:34 +01:00
|
|
|
int c;
|
|
|
|
|
|
|
|
|
|
v = 0;
|
|
|
|
|
s = 0;
|
|
|
|
|
loop:
|
|
|
|
|
c = sgetc(f);
|
|
|
|
|
if (c == EOF) {
|
|
|
|
|
/* LCOV_EXCL_START */
|
|
|
|
|
return -1;
|
|
|
|
|
/* LCOV_EXCL_STOP */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
b = (unsigned char)c;
|
|
|
|
|
if ((b & 0x80) == 0) {
|
|
|
|
|
v |= (uint64_t)b << s;
|
|
|
|
|
s += 7;
|
2026-07-30 10:58:35 +02:00
|
|
|
if (tommy_unlikely(s >= 64)) {
|
2015-02-21 09:00:34 +01:00
|
|
|
/* LCOV_EXCL_START */
|
|
|
|
|
return -1;
|
|
|
|
|
/* LCOV_EXCL_STOP */
|
|
|
|
|
}
|
|
|
|
|
goto loop;
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-30 10:58:35 +02:00
|
|
|
/*
|
|
|
|
|
* Check for overflow: at s == 63 only 1 bit (0x01) fits in uint64_t
|
|
|
|
|
*/
|
|
|
|
|
if (tommy_unlikely(s == 63 && (b & 0x7e) != 0)) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-21 09:00:34 +01:00
|
|
|
v |= (uint64_t)(b & 0x7f) << s;
|
|
|
|
|
|
|
|
|
|
*value = v;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int sgetble32(STREAM* f, uint32_t* value)
|
|
|
|
|
{
|
|
|
|
|
unsigned char buf[4];
|
|
|
|
|
|
|
|
|
|
if (sread(f, buf, 4) != 0) {
|
|
|
|
|
/* LCOV_EXCL_START */
|
|
|
|
|
return -1;
|
|
|
|
|
/* LCOV_EXCL_STOP */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*value = buf[0] | (uint32_t)buf[1] << 8 | (uint32_t)buf[2] << 16 | (uint32_t)buf[3] << 24;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-03 16:50:31 +02:00
|
|
|
ssize_t sgetbs(STREAM* f, char* str, size_t size)
|
2015-02-21 09:00:34 +01:00
|
|
|
{
|
2026-06-04 00:34:51 +02:00
|
|
|
uint64_t len;
|
2015-02-21 09:00:34 +01:00
|
|
|
|
2026-06-04 00:34:51 +02:00
|
|
|
if (sgetb64(f, &len) < 0) {
|
2015-02-21 09:00:34 +01:00
|
|
|
/* LCOV_EXCL_START */
|
|
|
|
|
return -1;
|
|
|
|
|
/* LCOV_EXCL_STOP */
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-06 18:06:12 +02:00
|
|
|
if (len >= size) {
|
2015-02-21 09:00:34 +01:00
|
|
|
/* LCOV_EXCL_START */
|
|
|
|
|
return -1;
|
|
|
|
|
/* LCOV_EXCL_STOP */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
str[len] = 0;
|
|
|
|
|
|
2026-10-03 16:50:31 +02:00
|
|
|
if (sread(f, str, len) != 0) {
|
|
|
|
|
/* LCOV_EXCL_START */
|
|
|
|
|
return -1;
|
|
|
|
|
/* LCOV_EXCL_STOP */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return len;
|
2015-02-21 09:00:34 +01:00
|
|
|
}
|
|
|
|
|
|
2026-09-09 18:50:39 +02:00
|
|
|
int swrite_uncached(const void* void_data, size_t size, STREAM* f)
|
2015-02-21 09:00:34 +01:00
|
|
|
{
|
|
|
|
|
const unsigned char* data = void_data;
|
|
|
|
|
|
2026-09-09 18:50:39 +02:00
|
|
|
while (size) {
|
|
|
|
|
size_t available;
|
2015-02-21 09:00:34 +01:00
|
|
|
|
2026-09-09 18:50:39 +02:00
|
|
|
if (f->pos == f->end) {
|
|
|
|
|
if (sflush(f) != 0) {
|
2015-02-21 09:00:34 +01:00
|
|
|
/* LCOV_EXCL_START */
|
|
|
|
|
return -1;
|
|
|
|
|
/* LCOV_EXCL_STOP */
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-09-09 18:50:39 +02:00
|
|
|
|
|
|
|
|
available = (size_t)(f->end - f->pos);
|
|
|
|
|
if (available > size)
|
|
|
|
|
available = size;
|
|
|
|
|
|
|
|
|
|
memcpy(f->pos, data, available);
|
|
|
|
|
f->pos += available;
|
|
|
|
|
data += available;
|
|
|
|
|
size -= available;
|
2015-02-21 09:00:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int sputb32(uint32_t value, STREAM* s)
|
|
|
|
|
{
|
|
|
|
|
unsigned char b;
|
|
|
|
|
unsigned char buf[16];
|
|
|
|
|
unsigned i;
|
|
|
|
|
|
|
|
|
|
i = 0;
|
|
|
|
|
loop:
|
|
|
|
|
b = value & 0x7f;
|
|
|
|
|
value >>= 7;
|
|
|
|
|
|
|
|
|
|
if (value) {
|
|
|
|
|
buf[i++] = b;
|
|
|
|
|
goto loop;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
buf[i++] = b | 0x80;
|
|
|
|
|
|
|
|
|
|
return swrite(buf, i, s);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int sputb64(uint64_t value, STREAM* s)
|
|
|
|
|
{
|
|
|
|
|
unsigned char b;
|
|
|
|
|
unsigned char buf[16];
|
|
|
|
|
unsigned i;
|
|
|
|
|
|
|
|
|
|
i = 0;
|
|
|
|
|
loop:
|
|
|
|
|
b = value & 0x7f;
|
|
|
|
|
value >>= 7;
|
|
|
|
|
|
|
|
|
|
if (value) {
|
|
|
|
|
buf[i++] = b;
|
|
|
|
|
goto loop;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
buf[i++] = b | 0x80;
|
|
|
|
|
|
|
|
|
|
return swrite(buf, i, s);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int sputble32(uint32_t value, STREAM* s)
|
|
|
|
|
{
|
|
|
|
|
unsigned char buf[4];
|
|
|
|
|
|
|
|
|
|
buf[0] = value & 0xFF;
|
|
|
|
|
buf[1] = (value >> 8) & 0xFF;
|
|
|
|
|
buf[2] = (value >> 16) & 0xFF;
|
|
|
|
|
buf[3] = (value >> 24) & 0xFF;
|
|
|
|
|
|
|
|
|
|
return swrite(buf, 4, s);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int sputbs(const char* str, STREAM* f)
|
|
|
|
|
{
|
|
|
|
|
size_t len = strlen(str);
|
|
|
|
|
|
2026-06-04 00:34:51 +02:00
|
|
|
if (sputb64(len, f) != 0) {
|
2015-02-21 09:00:34 +01:00
|
|
|
/* LCOV_EXCL_START */
|
|
|
|
|
return -1;
|
|
|
|
|
/* LCOV_EXCL_STOP */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return swrite(str, len, f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if HAVE_FSYNC
|
|
|
|
|
int ssync(STREAM* s)
|
|
|
|
|
{
|
|
|
|
|
unsigned i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < s->handle_size; ++i) {
|
2026-09-05 09:51:38 +02:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* On macOS, standard fsync() only pushes data from kernel buffers to the
|
|
|
|
|
* drive's volatile cache without requesting a physical media flush. F_FULLFSYNC
|
|
|
|
|
* forces the drive controller to commit all cached data to permanent storage.
|
|
|
|
|
*/
|
|
|
|
|
#if defined(__APPLE__) && defined(F_FULLFSYNC)
|
|
|
|
|
ret = fcntl(s->handle[i].f, F_FULLFSYNC);
|
|
|
|
|
if (ret != 0 && (errno == ENOTSUP || errno == EINVAL))
|
|
|
|
|
ret = fsync(s->handle[i].f);
|
|
|
|
|
#else
|
|
|
|
|
ret = fsync(s->handle[i].f);
|
|
|
|
|
#endif
|
|
|
|
|
if (ret != 0) {
|
2015-02-21 09:00:34 +01:00
|
|
|
/* LCOV_EXCL_START */
|
|
|
|
|
s->state = STREAM_STATE_ERROR;
|
|
|
|
|
s->state_index = i;
|
|
|
|
|
return -1;
|
|
|
|
|
/* LCOV_EXCL_STOP */
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2026-09-07 10:56:48 +02:00
|
|
|
int smtime(STREAM* s, int64_t mtime_sec, int mtime_nsec)
|
|
|
|
|
{
|
|
|
|
|
unsigned i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < s->handle_size; ++i) {
|
|
|
|
|
if (s->handle[i].f != -1 && fmtime(s->handle[i].f, mtime_sec, mtime_nsec) != 0) {
|
|
|
|
|
/* LCOV_EXCL_START */
|
|
|
|
|
s->state = STREAM_STATE_ERROR;
|
|
|
|
|
s->state_index = i;
|
|
|
|
|
return -1;
|
|
|
|
|
/* LCOV_EXCL_STOP */
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|