Path: blob/master/libs/mpg123/src/libmpg123/lfs_wrap.c
4394 views
/*1lfs_wrap: Wrapper code for reader functions, both internal and external.23copyright 2010-2023 by the mpg123 project4free software under the terms of the LGPL 2.15see COPYING and AUTHORS files in distribution or http://mpg123.org67initially written by Thomas Orgis, thanks to Guido Draheim for consulting8and later manx for stirring it all up again910This file used to wrap 32 bit off_t client API to 64 bit off_t API. Now it is11mapping any user API with either off_t to portable int64_t API, while also12providing the guts of that also via a wrapper handle. This keeps the actual13read and seek implementation out of readers.c.1415See doc/LARGEFILE for the big picture.1617Note about file descriptors: We just assume that they are generally18interchangeable between large and small file code... and that a large file19descriptor will trigger errors when accessed with small file code where it20may cause trouble (a really large file).21*/2223#include "config.h"2425// Only activate the explicit largefile stuff here. The rest of the code shall26// work with abstract 64 bit offsets, or just plain default off_t (possibly27// using _FILE_OFFSET_BITS magic).28// Note that this macro does not influence normal off_t-using code.29#ifdef LFS_LARGEFILE_6430#define _LARGEFILE64_SOURCE31#endif3233// For correct MPG123_EXPORT.34#include "../common/abi_align.h"3536// Need the full header with non-portable API, for the bare mpg123_open*()37// declarations. But no renaming shenanigans.38#define MPG123_NO_LARGENAME39#include "mpg123.h"4041#include "lfs_wrap.h"42#include "../common/abi_align.h"43#include "../compat/compat.h"44#include <sys/stat.h>45#include <fcntl.h>4647#ifndef OFF_MAX48#undef OFF_MIN49#if SIZEOF_OFF_T == 450#define OFF_MAX INT32_MAX51#define OFF_MIN INT32_MIN52#elif SIZEOF_OFF_T == 853#define OFF_MAX INT64_MAX54#define OFF_MIN INT64_MIN55#else56#error "Unexpected width of off_t."57#endif58#endif5960// A paranoid check that someone did not define a wrong SIZEOF_OFF_T at configure time.61typedef unsigned char MPG123_STATIC_ASSERT[(SIZEOF_OFF_T == sizeof(off_t)) ? 1 : -1];6263#include "../common/debug.h"6465// We do not want to expose this publicly, but it is cleaner to have it also defined66// as portable API to offer the legacy function wrapper over. It's an undocumented67// function for int64_t arguments.68int attribute_align_arg mpg123_position64( mpg123_handle *fr, int64_t no, int64_t buffsize69, int64_t *current_frame, int64_t *frames_left70, double *current_seconds, double *seconds_left );7172/* I see that I will need custom data storage. Main use is for the replaced I/O later, but the seek table for small file offsets needs extra storage, too. */7374// The wrapper handle for descriptor and handle I/O.75// It is also used for storing a converted frame index.76// Plain portable API I/O should not need it at all.77#define IO_HANDLE64 0 /* no wrapping at all: client-provided callbacks */78#define IO_FD 1 /* default off_t callbacks */79#define IO_HANDLE 2 /* Wrapping over custom handle callbacks with off_t. */80#ifdef LFS_LARGEFILE_6481#define IO_FD_64 3 /* off64_t callbacks */82#define IO_HANDLE_64 4 /* ... with off64_t. */83#endif84#define IO_INT_FD 5 /* Internal I/O using a file descriptor and wrappers defined here. */8586struct wrap_data87{88/* Storage for small offset index table. */89#if SIZEOF_OFF_T == 490off_t *indextable;91// And ironically, another big offset table for mpg123_set_index_32.92// I wand to avoid having to change a line of code in the internals.93int64_t *set_indextable;94#endif95/* I/O handle stuff */96int iotype; // one of the above numbers97/* Data for IO_FD variants. */98int fd;99int my_fd; /* A descriptor that the wrapper code opened itself. */100#ifdef TIMEOUT_READ101time_t timeout_sec;102#endif103void* handle; // for IO_HANDLE variants104/* The actual callbacks from the outside. */105mpg123_ssize_t (*r_read) (int, void *, size_t);106off_t (*r_lseek)(int, off_t, int);107mpg123_ssize_t (*r_h_read)(void *, void *, size_t);108off_t (*r_h_lseek)(void*, off_t, int);109#ifdef LFS_LARGEFILE_64110mpg123_ssize_t (*r_read_64) (int, void *, size_t);111off64_t (*r_lseek_64)(int, off64_t, int);112mpg123_ssize_t (*r_h_read_64)(void *, void *, size_t);113off64_t (*r_h_lseek_64)(void*, off64_t, int);114#endif115void (*h_cleanup)(void*);116};117118/* Cleanup I/O part of the handle handle... but not deleting the wrapper handle itself.119That is stored in the frame and only deleted on mpg123_delete(). */120static void wrap_io_cleanup(void *handle)121{122struct wrap_data *ioh = handle;123debug("wrapper I/O cleanup");124if(ioh->iotype == IO_HANDLE125#ifdef LFS_LARGEFILE_64126|| ioh->iotype == IO_HANDLE_64127#endif128){129if(ioh->h_cleanup != NULL && ioh->handle != NULL)130{131mdebug("calling client handle cleanup %p", ioh->handle);132ioh->h_cleanup(ioh->handle);133}134ioh->handle = NULL;135}136if(ioh->my_fd >= 0)137{138mdebug("closing my fd %d", ioh->my_fd);139#if defined(MPG123_COMPAT_MSVCRT_IO)140_close(ioh->my_fd);141#else142close(ioh->my_fd);143#endif144ioh->my_fd = -1;145}146}147148/* Really finish off the handle... freeing all memory. */149void INT123_wrap_destroy(void *handle)150{151struct wrap_data *wh = handle;152if(!wh)153return;154wrap_io_cleanup(handle);155#if SIZEOF_OFF_T == 4156if(wh->indextable != NULL)157free(wh->indextable);158if(wh->set_indextable != NULL)159free(wh->set_indextable);160#endif161162free(wh);163}164165/* More helper code... extract the special wrapper handle, possible allocate and initialize it. */166static struct wrap_data* wrap_get(mpg123_handle *mh, int force_alloc)167{168struct wrap_data* whd;169void ** whd_ = INT123_wrap_handle(mh);170171if(whd_ == NULL)172return NULL;173174/* Access the private storage inside the mpg123 handle.175The real callback functions and handles are stored there. */176if(*whd_ == NULL && force_alloc)177{178/* Create a new one. */179*whd_ = malloc(sizeof(struct wrap_data));180if(*whd_ == NULL)181{182INT123_set_err(mh, MPG123_OUT_OF_MEM);183return NULL;184}185whd = *whd_;186#if SIZEOF_OFF_T == 4187whd->indextable = NULL;188whd->set_indextable = NULL;189#endif190whd->iotype = IO_HANDLE64; // By default, the I/O path is not affected.191whd->fd = -1;192whd->my_fd = -1;193whd->handle = NULL;194whd->r_read = NULL;195whd->r_lseek = NULL;196whd->r_h_read = NULL;197whd->r_h_lseek = NULL;198#ifdef LFS_LARGEFILE_64199whd->r_read_64 = NULL;200whd->r_lseek_64 = NULL;201whd->r_h_read_64 = NULL;202whd->r_h_lseek_64 = NULL;203#endif204whd->h_cleanup = NULL;205}206else whd = *whd_;207208return whd;209}210211/* After settling the data... start with some simple wrappers. */212213// The first block of wrappers is always present, using the native off_t width.214// (Exception: If explicitly disabled using FORCED_OFF_64.)215// A second block mirrors that in case of sizeof(off_t)==4 with _32 suffix.216// A third block follows if 64 bit off_t is available with _64 suffix, just aliasing217// the int64_t functions.218219#ifndef FORCED_OFF_64220221#define OFF_CONV(value, variable, handle) \222if((value) >= OFF_MIN && (value) <= OFF_MAX) \223variable = (off_t)(value); \224else return INT123_set_err(handle, MPG123_LFS_OVERFLOW);225226#define OFF_CONVP(value, varpointer, handle) \227if(varpointer){ OFF_CONV(value, *(varpointer), handle) }228229#define OFF_RETURN(value, handle) \230return ((value) >= OFF_MIN && (value) <= OFF_MAX) \231? (off_t)(value) \232: INT123_set_err(handle, MPG123_LFS_OVERFLOW);233234int attribute_align_arg mpg123_framebyframe_decode(mpg123_handle *mh, off_t *num, unsigned char **audio, size_t *bytes)235{236int64_t fnum = 0;237int ret = mpg123_framebyframe_decode64(mh, &fnum, audio, bytes);238OFF_CONVP(fnum, num, mh)239return ret;240}241242int attribute_align_arg mpg123_decode_frame(mpg123_handle *mh, off_t *num, unsigned char **audio, size_t *bytes)243{244int64_t fnum = 0;245int ret = mpg123_decode_frame64(mh, &fnum, audio, bytes);246OFF_CONVP(fnum, num, mh)247return ret;248}249250off_t attribute_align_arg mpg123_timeframe(mpg123_handle *mh, double seconds)251{252int64_t b = mpg123_timeframe64(mh, seconds);253OFF_RETURN(b, mh)254}255256off_t attribute_align_arg mpg123_tell(mpg123_handle *mh)257{258int64_t pos = mpg123_tell64(mh);259OFF_RETURN(pos, mh)260}261262off_t attribute_align_arg mpg123_tellframe(mpg123_handle *mh)263{264int64_t frame = mpg123_tellframe64(mh);265OFF_RETURN(frame, mh)266}267268off_t attribute_align_arg mpg123_tell_stream(mpg123_handle *mh)269{270int64_t off = mpg123_tell_stream64(mh);271OFF_RETURN(off, mh)272}273274off_t attribute_align_arg mpg123_seek(mpg123_handle *mh, off_t sampleoff, int whence)275{276int64_t ret = mpg123_seek64(mh, (int64_t)sampleoff, whence);277OFF_RETURN(ret, mh)278}279280off_t attribute_align_arg mpg123_feedseek(mpg123_handle *mh, off_t sampleoff, int whence, off_t *input_offset)281{282int64_t inoff = 0;283int64_t ret = mpg123_feedseek64(mh, (int64_t)sampleoff, whence, &inoff);284OFF_CONVP(inoff, input_offset, mh)285OFF_RETURN(ret, mh)286}287288off_t attribute_align_arg mpg123_seek_frame(mpg123_handle *mh, off_t offset, int whence)289{290int64_t ret = mpg123_seek_frame64(mh, (int64_t)offset, whence);291OFF_RETURN(ret, mh)292}293294int attribute_align_arg mpg123_set_filesize(mpg123_handle *mh, off_t size)295{296return mpg123_set_filesize64(mh, (int64_t)size);297}298299off_t attribute_align_arg mpg123_framelength(mpg123_handle *mh)300{301int64_t ret = mpg123_framelength64(mh);302OFF_RETURN(ret, mh)303}304305off_t attribute_align_arg mpg123_length(mpg123_handle *mh)306{307int64_t ret = mpg123_length64(mh);308OFF_RETURN(ret, mh)309}310311// Native off_t is either identical to int32_t or int64_t.312// If the former, we create a copy of the index table.313int attribute_align_arg mpg123_index(mpg123_handle *mh, off_t **offsets, off_t *step, size_t *fill)314{315#if SIZEOF_OFF_T == 8316return mpg123_index64(mh, (int64_t**)offsets, (int64_t*)step, fill);317#else318int err;319int64_t largestep;320int64_t *largeoffsets;321struct wrap_data *whd;322if(mh == NULL)323return MPG123_BAD_HANDLE;324if(offsets == NULL || step == NULL || fill == NULL)325return INT123_set_err(mh, MPG123_BAD_INDEX_PAR);326*fill = 0; // better safe than sorry327328whd = wrap_get(mh, 1);329if(whd == NULL) return MPG123_ERR;330331err = mpg123_index64(mh, &largeoffsets, &largestep, fill);332if(err != MPG123_OK) return err;333334/* For a _very_ large file, even the step could overflow. */335OFF_CONV(largestep, *step, mh);336337/* When there are no values stored, there is no table content to take care of.338Table pointer does not matter. Mission completed. */339if(*fill == 0) return MPG123_OK;340341/* Construct a copy of the index to hand over to the small-minded client. */342*offsets = INT123_safe_realloc(whd->indextable, (*fill)*sizeof(int32_t));343if(*offsets == NULL)344return INT123_set_err(mh, MPG123_OUT_OF_MEM);345whd->indextable = *offsets;346/* Elaborate conversion of each index value, with overflow check. */347for(size_t i=0; i<*fill; ++i)348OFF_CONV(largeoffsets[i], whd->indextable[i], mh);349/* If we came that far... there should be a valid copy of the table now. */350return MPG123_OK;351#endif352}353354int attribute_align_arg mpg123_set_index(mpg123_handle *mh, off_t *offsets, off_t step, size_t fill)355{356#if SIZEOF_OFF_T == 8357return mpg123_set_index64(mh, (int64_t*)offsets, (int64_t)step, fill);358#else359int err;360struct wrap_data *whd;361int64_t *indextmp;362if(mh == NULL) return MPG123_BAD_HANDLE;363364whd = wrap_get(mh, 1);365if(whd == NULL) return MPG123_ERR;366367if(fill > 0 && offsets == NULL)368return INT123_set_err(mh, MPG123_BAD_INDEX_PAR);369else370{371/* Expensive temporary storage... for staying outside at the API layer. */372indextmp = INT123_safe_realloc(whd->set_indextable, fill*sizeof(int64_t));373if(indextmp == NULL)374return INT123_set_err(mh, MPG123_OUT_OF_MEM);375whd->set_indextable = indextmp;376/* Fill the large-file copy of the provided index, then feed it to mpg123. */377for(size_t i=0; i<fill; ++i)378indextmp[i] = offsets[i];379err = mpg123_set_index64(mh, indextmp, (int64_t)step, fill);380}381382return err;383#endif384}385386off_t attribute_align_arg mpg123_framepos(mpg123_handle *mh)387{388int64_t pos = mpg123_framepos64(mh);389OFF_RETURN(pos, mh)390}391392int attribute_align_arg mpg123_position( mpg123_handle *mh, off_t INT123_frame_offset393, off_t buffered_bytes, off_t *current_frame, off_t *frames_left394, double *current_seconds, double *seconds_left )395{396int64_t curframe, frameleft;397int err;398399err = mpg123_position64( mh, (int64_t)INT123_frame_offset, (int64_t)buffered_bytes400, &curframe, &frameleft, current_seconds, seconds_left );401if(err != MPG123_OK) return err;402403OFF_CONVP(curframe, current_frame, mh)404OFF_CONVP(frameleft, frames_left, mh);405return MPG123_OK;406}407408#endif // FORCED_OFF_64409410// _32 aliases only for native 32 bit off_t411// Will compilers be smart enough to optimize away the extra function call?412#if SIZEOF_OFF_T == 4413414// The open routines are trivial now. I only have differeing symbols suffixes415// to keep legacy ABI.416417int attribute_align_arg mpg123_open_32(mpg123_handle *mh, const char *path)418{419return mpg123_open(mh, path);420}421422int attribute_align_arg mpg123_open_fixed_32( mpg123_handle *mh, const char *path423, int channels, int encoding )424{425return mpg123_open_fixed(mh, path, channels, encoding);426}427428int attribute_align_arg mpg123_open_fd_32(mpg123_handle *mh, int fd)429{430return mpg123_open_fd(mh, fd);431}432433int attribute_align_arg mpg123_open_handle_32(mpg123_handle *mh, void *iohandle)434{435return mpg123_open_handle(mh, iohandle);436}437438int attribute_align_arg mpg123_framebyframe_decode_32(mpg123_handle *mh, off_t *num, unsigned char **audio, size_t *bytes)439{440return mpg123_framebyframe_decode(mh, num, audio, bytes);441}442443int attribute_align_arg mpg123_decode_frame_32(mpg123_handle *mh, off_t *num, unsigned char **audio, size_t *bytes)444{445return mpg123_decode_frame(mh, num, audio, bytes);446}447448off_t attribute_align_arg mpg123_timeframe_32(mpg123_handle *mh, double seconds)449{450return mpg123_timeframe64(mh, seconds);451}452453off_t attribute_align_arg mpg123_tell_32(mpg123_handle *mh)454{455return mpg123_tell(mh);456}457458off_t attribute_align_arg mpg123_tellframe_32(mpg123_handle *mh)459{460return mpg123_tellframe(mh);461}462463off_t attribute_align_arg mpg123_tell_stream_32(mpg123_handle *mh)464{465return mpg123_tell_stream(mh);466}467468off_t attribute_align_arg mpg123_seek_32(mpg123_handle *mh, off_t sampleoff, int whence)469{470return mpg123_seek(mh, sampleoff, whence);471}472473off_t attribute_align_arg mpg123_feedseek_32(mpg123_handle *mh, off_t sampleoff, int whence, off_t *input_offset)474{475return mpg123_feedseek(mh, sampleoff, whence, input_offset);476}477478off_t attribute_align_arg mpg123_seek_frame_32(mpg123_handle *mh, off_t offset, int whence)479{480return mpg123_seek_frame(mh, offset, whence);481}482483int attribute_align_arg mpg123_set_filesize_32(mpg123_handle *mh, off_t size)484{485return mpg123_set_filesize(mh, size);486}487488off_t attribute_align_arg mpg123_framelength_32(mpg123_handle *mh)489{490return mpg123_framelength(mh);491}492493off_t attribute_align_arg mpg123_length_32(mpg123_handle *mh)494{495return mpg123_length(mh);496}497498int attribute_align_arg mpg123_index_32(mpg123_handle *mh, off_t **offsets, off_t *step, size_t *fill)499{500return mpg123_index(mh, offsets, step, fill);501}502503int attribute_align_arg mpg123_set_index_32(mpg123_handle *mh, off_t *offsets, off_t step, size_t fill)504{505return mpg123_set_index(mh, offsets, step, fill);506}507508off_t attribute_align_arg mpg123_framepos_32(mpg123_handle *mh)509{510return mpg123_framepos(mh);511}512513int attribute_align_arg mpg123_position_32( mpg123_handle *mh, off_t INT123_frame_offset514, off_t buffered_bytes, off_t *current_frame, off_t *frames_left515, double *current_seconds, double *seconds_left )516{517return mpg123_position( mh, INT123_frame_offset, buffered_bytes518, current_frame, frames_left, current_seconds, seconds_left );519}520521#endif522523// _64 aliases if we either got some off64_t to work with or524// if there is no explicit 64 bit API but off_t is just always525// 64 bits.526#if defined(LFS_LARGEFILE_64) || (SIZEOF_OFF_T == 8)527528#ifdef LFS_LARGEFILE_64529#define OFF64 off64_t530#else531#define OFF64 off_t532#endif533534#ifndef FORCED_OFF_64535// When 64 bit offsets are enforced, libmpg123.c defines the _64 functions directly.536// There is no actual wrapper work, anyway.537538int attribute_align_arg mpg123_open_64(mpg123_handle *mh, const char *path)539{540return mpg123_open(mh, path);541}542543int attribute_align_arg mpg123_open_fixed_64( mpg123_handle *mh, const char *path544, int channels, int encoding )545{546return mpg123_open_fixed(mh, path, channels, encoding);547}548549int attribute_align_arg mpg123_open_fd_64(mpg123_handle *mh, int fd)550{551return mpg123_open_fd(mh, fd);552}553554int attribute_align_arg mpg123_open_handle_64(mpg123_handle *mh, void *iohandle)555{556return mpg123_open_handle(mh, iohandle);557}558#endif559560561int attribute_align_arg mpg123_framebyframe_decode_64(mpg123_handle *mh, OFF64 *num, unsigned char **audio, size_t *bytes)562{563return mpg123_framebyframe_decode64(mh, (int64_t*)num, audio, bytes);564}565566int attribute_align_arg mpg123_decode_frame_64(mpg123_handle *mh, OFF64 *num, unsigned char **audio, size_t *bytes)567{568return mpg123_decode_frame64(mh, (int64_t*)num, audio, bytes);569}570571OFF64 attribute_align_arg mpg123_timeframe_64(mpg123_handle *mh, double seconds)572{573return mpg123_timeframe64(mh, seconds);574}575576OFF64 attribute_align_arg mpg123_tell_64(mpg123_handle *mh)577{578return mpg123_tell64(mh);579}580581OFF64 attribute_align_arg mpg123_tellframe_64(mpg123_handle *mh)582{583return mpg123_tellframe64(mh);584}585586OFF64 attribute_align_arg mpg123_tell_stream_64(mpg123_handle *mh)587{588return mpg123_tell_stream64(mh);589}590591OFF64 attribute_align_arg mpg123_seek_64(mpg123_handle *mh, OFF64 sampleoff, int whence)592{593return mpg123_seek64(mh, (int64_t)sampleoff, whence);594}595596OFF64 attribute_align_arg mpg123_feedseek_64(mpg123_handle *mh, OFF64 sampleoff, int whence, OFF64 *input_offset)597{598return mpg123_feedseek64(mh, (int64_t)sampleoff, whence, (int64_t*)input_offset);599}600601OFF64 attribute_align_arg mpg123_seek_frame_64(mpg123_handle *mh, OFF64 offset, int whence)602{603return mpg123_seek_frame64(mh, (int64_t)offset, whence);604}605606int attribute_align_arg mpg123_set_filesize_64(mpg123_handle *mh, OFF64 size)607{608return mpg123_set_filesize64(mh, (int64_t)size);609}610611OFF64 attribute_align_arg mpg123_framelength_64(mpg123_handle *mh)612{613return mpg123_framelength64(mh);614}615616OFF64 attribute_align_arg mpg123_length_64(mpg123_handle *mh)617{618return mpg123_length64(mh);619}620621int attribute_align_arg mpg123_index_64(mpg123_handle *mh, OFF64 **offsets, OFF64 *step, size_t *fill)622{623return mpg123_index64(mh, (int64_t**)offsets, (int64_t*)step, fill);624}625626int attribute_align_arg mpg123_set_index_64(mpg123_handle *mh, OFF64 *offsets, OFF64 step, size_t fill)627{628return mpg123_set_index64(mh, (int64_t*)offsets, (int64_t)step, fill);629}630631OFF64 attribute_align_arg mpg123_framepos_64(mpg123_handle *mh)632{633return mpg123_framepos64(mh);634}635636int attribute_align_arg mpg123_position_64( mpg123_handle *mh, OFF64 INT123_frame_offset637, OFF64 buffered_bytes, OFF64 *current_frame, OFF64 *frames_left638, double *current_seconds, double *seconds_left )639{640return mpg123_position64( mh, (int64_t)INT123_frame_offset, (int64_t)buffered_bytes641, (int64_t*)current_frame, (int64_t*)frames_left, current_seconds, seconds_left );642}643644#undef OFF64645#endif646647/* =========================================648THE BOUNDARY OF SANITY649Behold, stranger!650========================================= */651652// One read callback wrapping over all 4 client callback variants.653static int wrap_read(void* handle, void *buf, size_t count, size_t *got)654{655struct wrap_data *ioh = handle;656ptrdiff_t retgot = -1;657switch(ioh->iotype)658{659case IO_FD:660retgot = ioh->r_read(ioh->fd, buf, count);661break;662case IO_HANDLE:663retgot = ioh->r_h_read(ioh->handle, buf, count);664break;665#ifdef LFS_LARGEFILE_64666case IO_FD_64:667retgot = ioh->r_read_64(ioh->fd, buf, count);668break;669case IO_HANDLE_64:670retgot = ioh->r_h_read_64(ioh->handle, buf, count);671break;672#endif673default:674error("Serious breakage - bad IO type in LFS wrapper!");675}676if(got)677*got = retgot > 0 ? (size_t)retgot : 0;678return retgot >= 0 ? 0 : -1;679}680681// One seek callback wrapping over all 4 client callback variants.682static int64_t wrap_lseek(void *handle, int64_t offset, int whence)683{684struct wrap_data *ioh = handle;685686if( (ioh->iotype == IO_FD || ioh->iotype == IO_HANDLE) &&687(offset < OFF_MIN || offset > OFF_MAX) )688{689errno = EOVERFLOW;690return -1;691}692switch(ioh->iotype)693{694case IO_FD: return ioh->r_lseek(ioh->fd, (off_t)offset, whence);695case IO_HANDLE: return ioh->r_h_lseek(ioh->handle, (off_t)offset, whence);696#ifdef LFS_LARGEFILE_64697case IO_FD_64: return ioh->r_lseek_64(ioh->fd, offset, whence);698case IO_HANDLE_64: return ioh->r_h_lseek_64(ioh->handle, offset, whence);699#endif700}701error("Serious breakage - bad IO type in LFS wrapper!");702return -1;703}704705// Defining a wrapper to the native read to be sure the prototype matches.706// There are platforms where it is read(int, void*, unsigned int).707// We know that we read small chunks where the difference does not matter. Could708// apply specific hackery, use a common compat_read() (INT123_unintr_read()?) with system709// specifics.710static mpg123_ssize_t fallback_read(int fd, void *buf, size_t count)711{712#if defined(MPG123_COMPAT_MSVCRT_IO)713if(count > UINT_MAX)714{715errno = EOVERFLOW;716return -1;717}718return _read(fd, buf, (unsigned int)count);719#else720return read(fd, buf, count);721#endif722}723724static off_t fallback_lseek(int fd, off_t offset, int whence)725{726#if defined(MPG123_COMPAT_MSVCRT_IO)727// Off_t is 32 bit and does fit into long. We know that.728return _lseek(fd, (long)offset, whence);729#else730return lseek(fd, offset, whence);731#endif732}733734// This is assuming an internally opened file, which usually will be735// using 64 bit offsets. It keeps reading on on trivial interruptions.736// I guess any file descriptor that matches the libc should work fine.737static int internal_read64(void *handle, void *buf, size_t bytes, size_t *got_bytes)738{739int ret = 0;740if(!handle || (!buf && bytes))741return -1;742struct wrap_data* ioh = handle;743int fd = ioh->fd;744size_t got = 0;745errno = 0;746while(bytes)747{748#ifdef TIMEOUT_READ749if(ioh->timeout_sec)750{751fd_set fds;752tv.tv_sec = ioh->timeout_sec;753tv.tv_usec = 0;754FD_ZERO(&fds);755FD_SET(fd, &fds);756int sret = select(fd+1, &fds, NULL, NULL, &tv);757if(sret < 1)758{759return -1; // timeout means error760// communicate quietness flag? if(NOQUIET) error("stream timed out");761}762}763#endif764errno = 0;765ptrdiff_t part = fallback_read(fd, (char*)buf+got, bytes);766if(part > 0) // == 0 is end of file767{768SATURATE_SUB(bytes, part, 0)769SATURATE_ADD(got, part, SIZE_MAX)770} else if(errno != EINTR && errno != EAGAIN771#if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN)772&& errno != EWOULDBLOCK773#endif774){775if(part < 0)776ret = -1;777break;778}779}780if(got_bytes)781*got_bytes = got;782return ret;783}784785static int64_t internal_lseek64(void *handle, int64_t offset, int whence)786{787struct wrap_data* ioh = handle;788#ifdef LFS_LARGEFILE_64789return lseek64(ioh->fd, offset, whence);790#elif defined(MPG123_COMPAT_MSVCRT_IO_64)791return _lseeki64(ioh->fd, offset, whence);792#else793if(offset < OFF_MIN || offset > OFF_MAX)794{795errno = EOVERFLOW;796return -1;797}798return fallback_lseek(ioh->fd, (off_t)offset, whence);799#endif800}801802int INT123_wrap_open(mpg123_handle *mh, void *handle, const char *path, int fd, long timeout, int quiet)803{804int force_alloc = (path || fd >= 0) ? 1 : 0;805struct wrap_data *ioh = wrap_get(mh, force_alloc);806if(ioh == NULL && force_alloc)807return MPG123_ERR;808if(!path && fd < 0)809{810if(!ioh || ioh->iotype == IO_HANDLE64)811{812mdebug("user-supplied 64 bit I/O on user-supplied handle %p", handle);813return LFS_WRAP_NONE;814}815if(ioh->iotype == IO_HANDLE)816{817mdebug("wrapped user handle %p", handle);818ioh->handle = handle;819if(ioh->r_h_read && ioh->r_h_lseek)820return mpg123_reader64(mh, wrap_read, wrap_lseek, wrap_io_cleanup);821return INT123_set_err(mh, MPG123_NO_READER);822}823#ifdef LFS_LARGEFILE_64824if(ioh->iotype == IO_HANDLE_64)825{826mdebug("wrapped 64 bit user handle %p", handle);827ioh->handle = handle;828if(ioh->r_h_read_64 && ioh->r_h_lseek_64)829return mpg123_reader64(mh, wrap_read, wrap_lseek, wrap_io_cleanup);830return INT123_set_err(mh, MPG123_NO_READER);831}832#endif833}834if(path)835{836debug("opening path (providing fd)");837// Open the resource and store the descriptor for closing later.838int flags=O_RDONLY;839#ifdef O_BINARY840flags |= O_BINARY;841#endif842#if defined(LFS_LARGEFILE_64) && defined(HAVE_O_LARGEFILE)843flags |= O_LARGEFILE;844#endif845errno = 0;846ioh->my_fd = fd = INT123_compat_open(path, flags);847if(fd < 0)848{849if(!quiet)850error2("Cannot open file %s: %s", path, INT123_strerror(errno));851return INT123_set_err(mh, MPG123_BAD_FILE);852}853}854if(fd >= 0)855{856mdebug("working with given fd %d", fd);857ioh->fd = fd;858// Prepared I/O using external callbacks.859if(ioh->iotype == IO_FD)860{861debug("native fd callbacks");862if(ioh->r_read && ioh->r_lseek)863return mpg123_reader64(mh, wrap_read, wrap_lseek, wrap_io_cleanup);864return INT123_set_err(mh, MPG123_NO_READER);865}866#ifdef LFS_LARGEFILE_64867if(ioh->iotype == IO_FD_64)868{869debug("64 bit fd callbacks");870if(ioh->r_read_64 && ioh->r_lseek_64)871return mpg123_reader64(mh, wrap_read, wrap_lseek, wrap_io_cleanup);872return INT123_set_err(mh, MPG123_NO_READER);873}874debug("internal 64 bit I/O");875#else876debug("internal 32-behind-64 bit I/O");877#endif878// Doing our own thing using the given/just-opened descriptor.879ioh->iotype = IO_INT_FD;880#ifdef TIMEOUT_READ881ioh->timeout_sec = (time_t)(timeout > 0 ? timeout : 0);882if(ioh->timeout_sec > 0)883{884mdebug("timeout reader with %ld s", timeout);885int flags;886flags = fcntl(fd, F_GETFL);887flags |= O_NONBLOCK;888fcntl(fd, F_SETFL, flags);889}890#endif891return mpg123_reader64(mh, internal_read64, internal_lseek64, wrap_io_cleanup);892}893return MPG123_ERR;894}895896// So, native off_t reader replacement.897898// In forced 64 bit offset mode, the only definitions of these are899// the _64 ones.900#ifdef FORCED_OFF_64901#define mpg123_replace_reader mpg123_replace_reader_64902#define mpg123_replace_reader_handle mpg123_replace_reader_handle_64903#endif904905/* Reader replacement prepares the hidden handle storage for next mpg123_open_fd() or plain mpg123_open(). */906int attribute_align_arg mpg123_replace_reader(mpg123_handle *mh, mpg123_ssize_t (*r_read) (int, void *, size_t), off_t (*r_lseek)(int, off_t, int) )907{908struct wrap_data* ioh;909910if(mh == NULL) return MPG123_ERR;911912mpg123_close(mh);913ioh = wrap_get(mh, 1);914if(ioh == NULL) return MPG123_ERR;915916/* If both callbacks are NULL, switch totally to internal I/O, else just use fallback for at most half of them. */917if(r_read == NULL && r_lseek == NULL)918{919ioh->iotype = IO_INT_FD;920ioh->fd = -1;921ioh->r_read = NULL;922ioh->r_lseek = NULL;923}924else925{926ioh->iotype = IO_FD;927ioh->fd = -1; /* On next mpg123_open_fd(), this gets a value. */928ioh->r_read = r_read != NULL ? r_read : fallback_read;929ioh->r_lseek = r_lseek != NULL ? r_lseek : fallback_lseek;930}931932/* The real reader replacement will happen while opening. */933return MPG123_OK;934}935936int attribute_align_arg mpg123_replace_reader_handle(mpg123_handle *mh, mpg123_ssize_t (*r_read) (void*, void *, size_t), off_t (*r_lseek)(void*, off_t, int), void (*cleanup)(void*))937{938struct wrap_data* ioh;939940if(mh == NULL) return MPG123_ERR;941942mpg123_close(mh);943ioh = wrap_get(mh, 1);944if(ioh == NULL) return MPG123_ERR;945946ioh->iotype = IO_HANDLE;947ioh->handle = NULL;948ioh->r_h_read = r_read;949ioh->r_h_lseek = r_lseek;950ioh->h_cleanup = cleanup;951952/* The real reader replacement will happen while opening. */953return MPG123_OK;954}955956#if SIZEOF_OFF_T == 4957int attribute_align_arg mpg123_replace_reader_32(mpg123_handle *mh, mpg123_ssize_t (*r_read) (int, void *, size_t), off_t (*r_lseek)(int, off_t, int) )958{959return mpg123_replace_reader(mh, r_read, r_lseek);960}961962int attribute_align_arg mpg123_replace_reader_handle_32(mpg123_handle *mh, mpg123_ssize_t (*r_read) (void*, void *, size_t), off_t (*r_lseek)(void*, off_t, int), void (*cleanup)(void*))963{964return mpg123_replace_reader_handle(mh, r_read, r_lseek, cleanup);965}966967#endif968969#ifdef LFS_LARGEFILE_64970971int attribute_align_arg mpg123_replace_reader_64(mpg123_handle *mh, mpg123_ssize_t (*r_read) (int, void *, size_t), off64_t (*r_lseek)(int, off64_t, int) )972{973struct wrap_data* ioh;974975if(mh == NULL) return MPG123_ERR;976977mpg123_close(mh);978ioh = wrap_get(mh, 1);979if(ioh == NULL) return MPG123_ERR;980981/* If both callbacks are NULL, switch totally to internal I/O, else just use fallback for at most half of them. */982if(r_read == NULL && r_lseek == NULL)983{984ioh->iotype = IO_INT_FD;985ioh->fd = -1;986ioh->r_read_64 = NULL;987ioh->r_lseek_64 = NULL;988}989else990{991ioh->iotype = IO_FD_64;992ioh->fd = -1; /* On next mpg123_open_fd(), this gets a value. */993ioh->r_read_64 = r_read != NULL ? r_read : fallback_read;994ioh->r_lseek_64 = r_lseek != NULL ? r_lseek : lseek64;995}996997/* The real reader replacement will happen while opening. */998return MPG123_OK;999}10001001int attribute_align_arg mpg123_replace_reader_handle_64(mpg123_handle *mh, mpg123_ssize_t (*r_read) (void*, void *, size_t), off64_t (*r_lseek)(void*, off64_t, int), void (*cleanup)(void*))1002{1003struct wrap_data* ioh;10041005if(mh == NULL) return MPG123_ERR;10061007mpg123_close(mh);1008ioh = wrap_get(mh, 1);1009if(ioh == NULL) return MPG123_ERR;10101011ioh->iotype = IO_HANDLE_64;1012ioh->handle = NULL;1013ioh->r_h_read_64 = r_read;1014ioh->r_h_lseek_64 = r_lseek;1015ioh->h_cleanup = cleanup;10161017/* The real reader replacement will happen while opening. */1018return MPG123_OK;1019}10201021#elif SIZEOF_OFF_T == 810221023// If 64 bit off_t is enforced, libmpg123.c already defines the _64 functions.1024#ifndef FORCED_OFF_641025int attribute_align_arg mpg123_replace_reader_64(mpg123_handle *mh, mpg123_ssize_t (*r_read) (int, void *, size_t), off_t (*r_lseek)(int, off_t, int) )1026{1027return mpg123_replace_reader(mh, r_read, r_lseek);1028}10291030int attribute_align_arg mpg123_replace_reader_handle_64(mpg123_handle *mh, mpg123_ssize_t (*r_read) (void*, void *, size_t), off_t (*r_lseek)(void*, off_t, int), void (*cleanup)(void*))1031{1032return mpg123_replace_reader_handle(mh, r_read, r_lseek, cleanup);1033}1034#endif10351036#endif103710381039