/* **************************************************************************** * stream/builtin/file.c -- built-in FILE stream. * Copyright (C) 2016-2018 Thomas "Cakeisalie5" Touhey * * 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 . * ************************************************************************* */ #include "builtin.h" #ifndef LIBCASIO_DISABLED_FILE # include # include # include # if defined(__linux__) # include # 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