cake
/
libcasio
Archived
1
1
Fork 0
This repository has been archived on 2024-03-16. You can view files and clone it, but cannot push or open issues or pull requests.
libcasio/lib/stream/builtin/file.c

301 lines
6.5 KiB
C

/* ****************************************************************************
* stream/builtin/file.c -- built-in FILE stream.
* Copyright (C) 2016-2018 Thomas "Cakeisalie5" Touhey <thomas@touhey.fr>
*
* This file is part of libcasio.
* libcasio is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3.0 of the License,
* or (at your option) any later version.
*
* libcasio is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with libcasio; if not, see <http://www.gnu.org/licenses/>.
* ************************************************************************* */
#include "builtin.h"
#ifndef LIBCASIO_DISABLED_FILE
# include <stdlib.h>
# include <string.h>
# include <errno.h>
# if defined(__linux__)
# include <stdio_ext.h>
# else
# define __freadable(F) (1)
# define __fwritable(F) (1)
# endif
/* Cookie structure. */
typedef struct {
int _rstream_cl, _wstream_cl;
FILE *_rstream, *_wstream;
} file_cookie_t;
/* ---
* Callbacks.
* --- */
/**
* casio_file_read:
* Read from a FILE.
*
* @arg cookie the cookie.
* @arg data the data pointer
* @arg size the data size.
* @arg timeout the timeout.
* @return the error code (0 if ok).
*/
CASIO_LOCAL int casio_file_read(file_cookie_t *cookie,
unsigned char *dest, size_t size, unsigned int timeout)
{
size_t recv;
/* TODO: is there a way to set a timeout on this sort of read? */
(void)timeout;
/* Main receiving loop. */
recv = 0;
do {
/* Read. */
recv = fread(dest, 1, size, cookie->_rstream);
/* Iterate. */
dest = (void*)((char*)dest + recv);
size -= recv;
/* Check the error. */
if (!recv) {
/* Approximation. */
if (errno != EAGAIN)
break;
/* I'm absolutely unsure about this... */
msg((ll_info, "received EAGAIN, sleep and retry"));
errno = 0;
casio_sleep(3000);
continue;
}
} while (size);
/* Check the error. */
if (!recv) switch (errno) {
case 0:
/* End of file. */
msg((ll_error, "encountered an end of file"));
return (casio_error_eof);
case EINTR: /* alarm */
case ETIMEDOUT:
# ifdef ETIME
case ETIME:
# endif
/* A timeout has occurred. */
msg((ll_error, "timeout received"));
return (casio_error_timeout);
case ENODEV:
case EPIPE: case ESPIPE:
/* A device error has occured. */
msg((ll_error, "calculator was disconnected"));
return (casio_error_nocalc);
default:
msg((ll_fatal, "errno was %d: %s", errno, strerror(errno)));
return (casio_error_unknown);
}
return (0);
}
/**
* casio_file_write:
* Write to a FILE.
*
* @arg cookie the cookie.
* @arg data the source
* @arg size the source size
* @arg timeout the timeout.
* @return the error code (0 if ok)
*/
CASIO_LOCAL int casio_file_write(file_cookie_t *cookie,
const unsigned char *data, size_t size, unsigned int timeout)
{
size_t sent;
/* TODO: is there a way to set a timeout on this sort of read? */
(void)timeout;
/* Main sending. */
sent = fwrite(data, size, 1, cookie->_wstream);
/* Check the error. */
if (!sent) switch (errno) {
case 0:
/* End of file (EOF). */
msg((ll_error, "encountered an end of file"));
return (casio_error_eof);
case EINTR: /* alarm */
case ETIMEDOUT:
# ifdef ETIME
case ETIME:
# endif
/* Timeout error. */
msg((ll_error, "timeout received"));
return (casio_error_timeout);
case ENODEV:
/* Device disconnected. */
msg((ll_fatal, "calculator was disconnected"));
return (casio_error_nocalc);
default:
msg((ll_fatal, "errno was %d: %s", errno, strerror(errno)));
return (casio_error_unknown);
}
return (0);
}
/**
* casio_file_seek:
* Seek within a file.
*
* @arg cookie the cookie.
* @arg offset the offset.
* @arg whence the whence.
* @return the error code (0 if ok).
*/
CASIO_LOCAL int casio_file_seek(file_cookie_t *cookie, casio_off_t *offset,
casio_whence_t whence)
{
int wh;
/* Seek. */
wh = whence == CASIO_SEEK_SET ? SEEK_SET
: whence == CASIO_SEEK_CUR ? SEEK_CUR
: SEEK_END;
if (fseek(cookie->_rstream, (long)*offset, wh) < 0)
return (casio_error_op); /* TODO: check errno? */
/* Hide. */
*offset = (casio_off_t)ftell(cookie->_rstream);
return (0);
}
/**
* casio_file_close:
* Close a FILE cookie.
*
* @arg vcookie the cookie.
* @return the error code (0 if ok)
*/
CASIO_LOCAL int casio_file_close(file_cookie_t *cookie)
{
if (cookie->_rstream && cookie->_rstream_cl)
fclose(cookie->_rstream);
if (cookie->_wstream && cookie->_wstream != cookie->_rstream
&& cookie->_wstream_cl)
fclose(cookie->_wstream);
casio_free(cookie);
return (0);
}
/* ---
* Opening functions.
* --- */
CASIO_LOCAL casio_streamfuncs_t const casio_file_callbacks = {
(casio_stream_close_t *)casio_file_close,
(casio_stream_read_t *)casio_file_read,
(casio_stream_write_t *)casio_file_write,
(casio_stream_seek_t *)casio_file_seek,
NULL, NULL, NULL, NULL
};
/**
* casio_open_stream_file:
* Open a FILE stream.
*
* @arg stream the stream to make.
* @arg rstream the FILE stream to read from.
* @arg wstream the FILe stream to write to.
* @arg rstream_cl should we close the read stream?
* @arg wstream_cl should we close the write stream?
* @return the error (0 if ok).
*/
int CASIO_EXPORT casio_open_stream_file(casio_stream_t **stream,
FILE *rstream, FILE *wstream, int rstream_cl, int wstream_cl)
{
file_cookie_t *cookie = NULL;
int err; casio_openmode_t mode = 0;
/* Setup the mode. */
if (rstream && __freadable(rstream))
mode |= CASIO_OPENMODE_READ;
if (wstream && __fwritable(wstream))
mode |= CASIO_OPENMODE_WRITE;
if (!mode) {
msg((ll_error, "Neither readable or writable..."));
err = casio_error_nostream;
goto fail;
}
/* Allocate the cookie. */
cookie = casio_alloc(1, sizeof(file_cookie_t));
if (!cookie) {
err = casio_error_alloc;
goto fail;
}
/* Fill the cookie. */
cookie->_rstream_cl = rstream_cl;
cookie->_wstream_cl = wstream_cl;
cookie->_rstream = rstream;
cookie->_wstream = wstream;
/* Initialize the stream. */
return (casio_open_stream(stream, mode, cookie, &casio_file_callbacks, 0));
fail:
if (rstream && rstream_cl)
fclose(rstream);
if (wstream != rstream && wstream_cl)
fclose(wstream);
return (err);
}
#endif