summaryrefslogtreecommitdiff
path: root/src/file_io.c
diff options
context:
space:
mode:
authorLorry <lorry@roadtrain.codethink.co.uk>2012-07-18 20:35:05 +0100
committerLorry <lorry@roadtrain.codethink.co.uk>2012-07-18 20:35:05 +0100
commitaceb1f30497619786f1827e4c65992b890a07410 (patch)
treece33ac9d0d076a8ec4f993e614cf71a4d5daa7e7 /src/file_io.c
downloadlibsndfile-aceb1f30497619786f1827e4c65992b890a07410.tar.gz
Tarball conversion
Diffstat (limited to 'src/file_io.c')
-rw-r--r--src/file_io.c1547
1 files changed, 1547 insertions, 0 deletions
diff --git a/src/file_io.c b/src/file_io.c
new file mode 100644
index 0000000..44f1b5a
--- /dev/null
+++ b/src/file_io.c
@@ -0,0 +1,1547 @@
+/*
+** Copyright (C) 2002-2011 Erik de Castro Lopo <erikd@mega-nerd.com>
+** Copyright (C) 2003 Ross Bencina <rbencina@iprimus.com.au>
+**
+** This program 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 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program 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 this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/*
+** The file is split into three sections as follows:
+** - The top section (USE_WINDOWS_API == 0) for Linux, Unix and MacOSX
+** systems (including Cygwin).
+** - The middle section (USE_WINDOWS_API == 1) for microsoft windows
+** (including MinGW) using the native windows API.
+** - A legacy windows section which attempted to work around grevious
+** bugs in microsoft's POSIX implementation.
+*/
+
+/*
+** The header file sfconfig.h MUST be included before the others to ensure
+** that large file support is enabled correctly on Unix systems.
+*/
+
+#include "sfconfig.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#if (HAVE_DECL_S_IRGRP == 0)
+#include <sf_unistd.h>
+#endif
+
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/stat.h>
+
+#include "sndfile.h"
+#include "common.h"
+
+#define SENSIBLE_SIZE (0x40000000)
+
+/*
+** Neat solution to the Win32/OS2 binary file flage requirement.
+** If O_BINARY isn't already defined by the inclusion of the system
+** headers, set it to zero.
+*/
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+static void psf_log_syserr (SF_PRIVATE *psf, int error) ;
+
+#if (USE_WINDOWS_API == 0)
+
+/*------------------------------------------------------------------------------
+** Win32 stuff at the bottom of the file. Unix and other sensible OSes here.
+*/
+
+static int psf_close_fd (int fd) ;
+static int psf_open_fd (PSF_FILE * pfile) ;
+static sf_count_t psf_get_filelen_fd (int fd) ;
+
+int
+psf_fopen (SF_PRIVATE *psf)
+{
+ psf->error = 0 ;
+ psf->file.filedes = psf_open_fd (&psf->file) ;
+
+ if (psf->file.filedes == - SFE_BAD_OPEN_MODE)
+ { psf->error = SFE_BAD_OPEN_MODE ;
+ psf->file.filedes = -1 ;
+ return psf->error ;
+ } ;
+
+ if (psf->file.filedes == -1)
+ psf_log_syserr (psf, errno) ;
+
+ return psf->error ;
+} /* psf_fopen */
+
+int
+psf_fclose (SF_PRIVATE *psf)
+{ int retval ;
+
+ if (psf->virtual_io)
+ return 0 ;
+
+ if (psf->file.do_not_close_descriptor)
+ { psf->file.filedes = -1 ;
+ return 0 ;
+ } ;
+
+ if ((retval = psf_close_fd (psf->file.filedes)) == -1)
+ psf_log_syserr (psf, errno) ;
+
+ psf->file.filedes = -1 ;
+
+ return retval ;
+} /* psf_fclose */
+
+int
+psf_open_rsrc (SF_PRIVATE *psf)
+{
+ if (psf->rsrc.filedes > 0)
+ return 0 ;
+
+ /* Test for MacOSX style resource fork on HPFS or HPFS+ filesystems. */
+ snprintf (psf->rsrc.path.c, sizeof (psf->rsrc.path.c), "%s/rsrc", psf->file.path.c) ;
+ psf->error = SFE_NO_ERROR ;
+ if ((psf->rsrc.filedes = psf_open_fd (&psf->rsrc)) >= 0)
+ { psf->rsrclength = psf_get_filelen_fd (psf->rsrc.filedes) ;
+ if (psf->rsrclength > 0 || (psf->rsrc.mode & SFM_WRITE))
+ return SFE_NO_ERROR ;
+ psf_close_fd (psf->rsrc.filedes) ;
+ psf->rsrc.filedes = -1 ;
+ } ;
+
+ if (psf->rsrc.filedes == - SFE_BAD_OPEN_MODE)
+ { psf->error = SFE_BAD_OPEN_MODE ;
+ return psf->error ;
+ } ;
+
+ /*
+ ** Now try for a resource fork stored as a separate file in the same
+ ** directory, but preceded with a dot underscore.
+ */
+ snprintf (psf->rsrc.path.c, sizeof (psf->rsrc.path.c), "%s._%s", psf->file.dir.c, psf->file.name.c) ;
+ psf->error = SFE_NO_ERROR ;
+ if ((psf->rsrc.filedes = psf_open_fd (&psf->rsrc)) >= 0)
+ { psf->rsrclength = psf_get_filelen_fd (psf->rsrc.filedes) ;
+ return SFE_NO_ERROR ;
+ } ;
+
+ /*
+ ** Now try for a resource fork stored in a separate file in the
+ ** .AppleDouble/ directory.
+ */
+ snprintf (psf->rsrc.path.c, sizeof (psf->rsrc.path.c), "%s.AppleDouble/%s", psf->file.dir.c, psf->file.name.c) ;
+ psf->error = SFE_NO_ERROR ;
+ if ((psf->rsrc.filedes = psf_open_fd (&psf->rsrc)) >= 0)
+ { psf->rsrclength = psf_get_filelen_fd (psf->rsrc.filedes) ;
+ return SFE_NO_ERROR ;
+ } ;
+
+ /* No resource file found. */
+ if (psf->rsrc.filedes == -1)
+ psf_log_syserr (psf, errno) ;
+
+ psf->rsrc.filedes = -1 ;
+
+ return psf->error ;
+} /* psf_open_rsrc */
+
+sf_count_t
+psf_get_filelen (SF_PRIVATE *psf)
+{ sf_count_t filelen ;
+
+ if (psf->virtual_io)
+ return psf->vio.get_filelen (psf->vio_user_data) ;
+
+ filelen = psf_get_filelen_fd (psf->file.filedes) ;
+
+ if (filelen == -1)
+ { psf_log_syserr (psf, errno) ;
+ return (sf_count_t) -1 ;
+ } ;
+
+ if (filelen == -SFE_BAD_STAT_SIZE)
+ { psf->error = SFE_BAD_STAT_SIZE ;
+ return (sf_count_t) -1 ;
+ } ;
+
+ switch (psf->file.mode)
+ { case SFM_WRITE :
+ filelen = filelen - psf->fileoffset ;
+ break ;
+
+ case SFM_READ :
+ if (psf->fileoffset > 0 && psf->filelength > 0)
+ filelen = psf->filelength ;
+ break ;
+
+ case SFM_RDWR :
+ /*
+ ** Cannot open embedded files SFM_RDWR so we don't need to
+ ** subtract psf->fileoffset. We already have the answer we
+ ** need.
+ */
+ break ;
+
+ default :
+ /* Shouldn't be here, so return error. */
+ filelen = -1 ;
+ } ;
+
+ return filelen ;
+} /* psf_get_filelen */
+
+int
+psf_close_rsrc (SF_PRIVATE *psf)
+{ psf_close_fd (psf->rsrc.filedes) ;
+ psf->rsrc.filedes = -1 ;
+ return 0 ;
+} /* psf_close_rsrc */
+
+int
+psf_set_stdio (SF_PRIVATE *psf)
+{ int error = 0 ;
+
+ switch (psf->file.mode)
+ { case SFM_RDWR :
+ error = SFE_OPEN_PIPE_RDWR ;
+ break ;
+
+ case SFM_READ :
+ psf->file.filedes = 0 ;
+ break ;
+
+ case SFM_WRITE :
+ psf->file.filedes = 1 ;
+ break ;
+
+ default :
+ error = SFE_BAD_OPEN_MODE ;
+ break ;
+ } ;
+ psf->filelength = 0 ;
+
+ return error ;
+} /* psf_set_stdio */
+
+void
+psf_set_file (SF_PRIVATE *psf, int fd)
+{ psf->file.filedes = fd ;
+} /* psf_set_file */
+
+int
+psf_file_valid (SF_PRIVATE *psf)
+{ return (psf->file.filedes >= 0) ? SF_TRUE : SF_FALSE ;
+} /* psf_set_file */
+
+sf_count_t
+psf_fseek (SF_PRIVATE *psf, sf_count_t offset, int whence)
+{ sf_count_t current_pos, new_position ;
+
+ if (psf->virtual_io)
+ return psf->vio.seek (offset, whence, psf->vio_user_data) ;
+
+ current_pos = psf_ftell (psf) ;
+
+ switch (whence)
+ { case SEEK_SET :
+ offset += psf->fileoffset ;
+ break ;
+
+ case SEEK_END :
+ if (psf->file.mode == SFM_WRITE)
+ { new_position = lseek (psf->file.filedes, offset, whence) ;
+
+ if (new_position < 0)
+ psf_log_syserr (psf, errno) ;
+
+ return new_position - psf->fileoffset ;
+ } ;
+
+ /* Transform SEEK_END into a SEEK_SET, ie find the file
+ ** length add the requested offset (should be <= 0) to
+ ** get the offset wrt the start of file.
+ */
+ whence = SEEK_SET ;
+ offset = lseek (psf->file.filedes, 0, SEEK_END) + offset ;
+ break ;
+
+ case SEEK_CUR :
+ /* Translate a SEEK_CUR into a SEEK_SET. */
+ offset += current_pos ;
+ whence = SEEK_SET ;
+ break ;
+
+ default :
+ /* We really should not be here. */
+ psf_log_printf (psf, "psf_fseek : whence is %d *****.\n", whence) ;
+ return 0 ;
+ } ;
+
+ if (current_pos != offset)
+ new_position = lseek (psf->file.filedes, offset, whence) ;
+ else
+ new_position = offset ;
+
+ if (new_position < 0)
+ psf_log_syserr (psf, errno) ;
+
+ new_position -= psf->fileoffset ;
+
+ return new_position ;
+} /* psf_fseek */
+
+sf_count_t
+psf_fread (void *ptr, sf_count_t bytes, sf_count_t items, SF_PRIVATE *psf)
+{ sf_count_t total = 0 ;
+ ssize_t count ;
+
+ if (psf->virtual_io)
+ return psf->vio.read (ptr, bytes*items, psf->vio_user_data) / bytes ;
+
+ items *= bytes ;
+
+ /* Do this check after the multiplication above. */
+ if (items <= 0)
+ return 0 ;
+
+ while (items > 0)
+ { /* Break the read down to a sensible size. */
+ count = (items > SENSIBLE_SIZE) ? SENSIBLE_SIZE : (ssize_t) items ;
+
+ count = read (psf->file.filedes, ((char*) ptr) + total, (size_t) count) ;
+
+ if (count == -1)
+ { if (errno == EINTR)
+ continue ;
+
+ psf_log_syserr (psf, errno) ;
+ break ;
+ } ;
+
+ if (count == 0)
+ break ;
+
+ total += count ;
+ items -= count ;
+ } ;
+
+ if (psf->is_pipe)
+ psf->pipeoffset += total ;
+
+ return total / bytes ;
+} /* psf_fread */
+
+sf_count_t
+psf_fwrite (const void *ptr, sf_count_t bytes, sf_count_t items, SF_PRIVATE *psf)
+{ sf_count_t total = 0 ;
+ ssize_t count ;
+
+ if (psf->virtual_io)
+ return psf->vio.write (ptr, bytes*items, psf->vio_user_data) / bytes ;
+
+ items *= bytes ;
+
+ /* Do this check after the multiplication above. */
+ if (items <= 0)
+ return 0 ;
+
+ while (items > 0)
+ { /* Break the writes down to a sensible size. */
+ count = (items > SENSIBLE_SIZE) ? SENSIBLE_SIZE : items ;
+
+ count = write (psf->file.filedes, ((const char*) ptr) + total, count) ;
+
+ if (count == -1)
+ { if (errno == EINTR)
+ continue ;
+
+ psf_log_syserr (psf, errno) ;
+ break ;
+ } ;
+
+ if (count == 0)
+ break ;
+
+ total += count ;
+ items -= count ;
+ } ;
+
+ if (psf->is_pipe)
+ psf->pipeoffset += total ;
+
+ return total / bytes ;
+} /* psf_fwrite */
+
+sf_count_t
+psf_ftell (SF_PRIVATE *psf)
+{ sf_count_t pos ;
+
+ if (psf->virtual_io)
+ return psf->vio.tell (psf->vio_user_data) ;
+
+ if (psf->is_pipe)
+ return psf->pipeoffset ;
+
+ pos = lseek (psf->file.filedes, 0, SEEK_CUR) ;
+
+ if (pos == ((sf_count_t) -1))
+ { psf_log_syserr (psf, errno) ;
+ return -1 ;
+ } ;
+
+ return pos - psf->fileoffset ;
+} /* psf_ftell */
+
+static int
+psf_close_fd (int fd)
+{ int retval ;
+
+ if (fd < 0)
+ return 0 ;
+
+ while ((retval = close (fd)) == -1 && errno == EINTR)
+ /* Do nothing. */ ;
+
+ return retval ;
+} /* psf_close_fd */
+
+sf_count_t
+psf_fgets (char *buffer, sf_count_t bufsize, SF_PRIVATE *psf)
+{ sf_count_t k = 0 ;
+ sf_count_t count ;
+
+ while (k < bufsize - 1)
+ { count = read (psf->file.filedes, &(buffer [k]), 1) ;
+
+ if (count == -1)
+ { if (errno == EINTR)
+ continue ;
+
+ psf_log_syserr (psf, errno) ;
+ break ;
+ } ;
+
+ if (count == 0 || buffer [k++] == '\n')
+ break ;
+ } ;
+
+ buffer [k] = 0 ;
+
+ return k ;
+} /* psf_fgets */
+
+int
+psf_is_pipe (SF_PRIVATE *psf)
+{ struct stat statbuf ;
+
+ if (psf->virtual_io)
+ return SF_FALSE ;
+
+ if (fstat (psf->file.filedes, &statbuf) == -1)
+ { psf_log_syserr (psf, errno) ;
+ /* Default to maximum safety. */
+ return SF_TRUE ;
+ } ;
+
+ if (S_ISFIFO (statbuf.st_mode) || S_ISSOCK (statbuf.st_mode))
+ return SF_TRUE ;
+
+ return SF_FALSE ;
+} /* psf_is_pipe */
+
+static sf_count_t
+psf_get_filelen_fd (int fd)
+{ struct stat statbuf ;
+
+ /*
+ ** Sanity check.
+ ** If everything is OK, this will be optimised out.
+ */
+ if (sizeof (statbuf.st_size) == 4 && sizeof (sf_count_t) == 8)
+ return (sf_count_t) -SFE_BAD_STAT_SIZE ;
+
+ if (fstat (fd, &statbuf) == -1)
+ return (sf_count_t) -1 ;
+
+ return statbuf.st_size ;
+} /* psf_get_filelen_fd */
+
+int
+psf_ftruncate (SF_PRIVATE *psf, sf_count_t len)
+{ int retval ;
+
+ /* Returns 0 on success, non-zero on failure. */
+ if (len < 0)
+ return -1 ;
+
+ if ((sizeof (off_t) < sizeof (sf_count_t)) && len > 0x7FFFFFFF)
+ return -1 ;
+
+ retval = ftruncate (psf->file.filedes, len) ;
+
+ if (retval == -1)
+ psf_log_syserr (psf, errno) ;
+
+ return retval ;
+} /* psf_ftruncate */
+
+void
+psf_init_files (SF_PRIVATE *psf)
+{ psf->file.filedes = -1 ;
+ psf->rsrc.filedes = -1 ;
+ psf->file.savedes = -1 ;
+} /* psf_init_files */
+
+void
+psf_use_rsrc (SF_PRIVATE *psf, int on_off)
+{
+ if (on_off)
+ { if (psf->file.filedes != psf->rsrc.filedes)
+ { psf->file.savedes = psf->file.filedes ;
+ psf->file.filedes = psf->rsrc.filedes ;
+ } ;
+ }
+ else if (psf->file.filedes == psf->rsrc.filedes)
+ psf->file.filedes = psf->file.savedes ;
+
+ return ;
+} /* psf_use_rsrc */
+
+static int
+psf_open_fd (PSF_FILE * pfile)
+{ int fd, oflag, mode ;
+
+ /*
+ ** Sanity check. If everything is OK, this test and the printfs will
+ ** be optimised out. This is meant to catch the problems caused by
+ ** "sfconfig.h" being included after <stdio.h>.
+ */
+ if (sizeof (off_t) != sizeof (sf_count_t))
+ { puts ("\n\n*** Fatal error : sizeof (off_t) != sizeof (sf_count_t)") ;
+ puts ("*** This means that libsndfile was not configured correctly.\n") ;
+ exit (1) ;
+ } ;
+
+ switch (pfile->mode)
+ { case SFM_READ :
+ oflag = O_RDONLY | O_BINARY ;
+ mode = 0 ;
+ break ;
+
+ case SFM_WRITE :
+ oflag = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY ;
+ mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH ;
+ break ;
+
+ case SFM_RDWR :
+ oflag = O_RDWR | O_CREAT | O_BINARY ;
+ mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH ;
+ break ;
+
+ default :
+ return - SFE_BAD_OPEN_MODE ;
+ break ;
+ } ;
+
+ if (mode == 0)
+ fd = open (pfile->path.c, oflag) ;
+ else
+ fd = open (pfile->path.c, oflag, mode) ;
+
+ return fd ;
+} /* psf_open_fd */
+
+static void
+psf_log_syserr (SF_PRIVATE *psf, int error)
+{
+ /* Only log an error if no error has been set yet. */
+ if (psf->error == 0)
+ { psf->error = SFE_SYSTEM ;
+ snprintf (psf->syserr, sizeof (psf->syserr), "System error : %s.", strerror (error)) ;
+ } ;
+
+ return ;
+} /* psf_log_syserr */
+
+void
+psf_fsync (SF_PRIVATE *psf)
+{
+#if HAVE_FSYNC
+ if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
+ fsync (psf->file.filedes) ;
+#else
+ psf = NULL ;
+#endif
+} /* psf_fsync */
+
+#elif USE_WINDOWS_API
+
+/* Win32 file i/o functions implemented using native Win32 API */
+
+#include <windows.h>
+#include <io.h>
+
+static int psf_close_handle (HANDLE handle) ;
+static HANDLE psf_open_handle (PSF_FILE * pfile) ;
+static sf_count_t psf_get_filelen_handle (HANDLE handle) ;
+
+/* USE_WINDOWS_API */ int
+psf_fopen (SF_PRIVATE *psf)
+{
+ psf->error = 0 ;
+ psf->file.handle = psf_open_handle (&psf->file) ;
+
+ if (psf->file.handle == NULL)
+ psf_log_syserr (psf, GetLastError ()) ;
+
+ return psf->error ;
+} /* psf_fopen */
+
+/* USE_WINDOWS_API */ int
+psf_fclose (SF_PRIVATE *psf)
+{ int retval ;
+
+ if (psf->virtual_io)
+ return 0 ;
+
+ if (psf->file.do_not_close_descriptor)
+ { psf->file.handle = NULL ;
+ return 0 ;
+ } ;
+
+ if ((retval = psf_close_handle (psf->file.handle)) == -1)
+ psf_log_syserr (psf, GetLastError ()) ;
+
+ psf->file.handle = NULL ;
+
+ return retval ;
+} /* psf_fclose */
+
+/* USE_WINDOWS_API */ int
+psf_open_rsrc (SF_PRIVATE *psf)
+{
+ if (psf->rsrc.handle != NULL)
+ return 0 ;
+
+ /* Test for MacOSX style resource fork on HPFS or HPFS+ filesystems. */
+ snprintf (psf->rsrc.path.c, sizeof (psf->rsrc.path.c), "%s/rsrc", psf->file.path.c) ;
+ psf->error = SFE_NO_ERROR ;
+ if ((psf->rsrc.handle = psf_open_handle (&psf->rsrc)) != NULL)
+ { psf->rsrclength = psf_get_filelen_handle (psf->rsrc.handle) ;
+ return SFE_NO_ERROR ;
+ } ;
+
+ /*
+ ** Now try for a resource fork stored as a separate file in the same
+ ** directory, but preceded with a dot underscore.
+ */
+ snprintf (psf->rsrc.path.c, sizeof (psf->rsrc.path.c), "%s._%s", psf->file.dir.c, psf->file.name.c) ;
+ psf->error = SFE_NO_ERROR ;
+ if ((psf->rsrc.handle = psf_open_handle (&psf->rsrc)) != NULL)
+ { psf->rsrclength = psf_get_filelen_handle (psf->rsrc.handle) ;
+ return SFE_NO_ERROR ;
+ } ;
+
+ /*
+ ** Now try for a resource fork stored in a separate file in the
+ ** .AppleDouble/ directory.
+ */
+ snprintf (psf->rsrc.path.c, sizeof (psf->rsrc.path.c), "%s.AppleDouble/%s", psf->file.dir.c, psf->file.name.c) ;
+ psf->error = SFE_NO_ERROR ;
+ if ((psf->rsrc.handle = psf_open_handle (&psf->rsrc)) != NULL)
+ { psf->rsrclength = psf_get_filelen_handle (psf->rsrc.handle) ;
+ return SFE_NO_ERROR ;
+ } ;
+
+ /* No resource file found. */
+ if (psf->rsrc.handle == NULL)
+ psf_log_syserr (psf, GetLastError ()) ;
+
+ psf->rsrc.handle = NULL ;
+
+ return psf->error ;
+} /* psf_open_rsrc */
+
+/* USE_WINDOWS_API */ sf_count_t
+psf_get_filelen (SF_PRIVATE *psf)
+{ sf_count_t filelen ;
+
+ if (psf->virtual_io)
+ return psf->vio.get_filelen (psf->vio_user_data) ;
+
+ filelen = psf_get_filelen_handle (psf->file.handle) ;
+
+ if (filelen == -1)
+ { psf_log_syserr (psf, errno) ;
+ return (sf_count_t) -1 ;
+ } ;
+
+ if (filelen == -SFE_BAD_STAT_SIZE)
+ { psf->error = SFE_BAD_STAT_SIZE ;
+ return (sf_count_t) -1 ;
+ } ;
+
+ switch (psf->file.mode)
+ { case SFM_WRITE :
+ filelen = filelen - psf->fileoffset ;
+ break ;
+
+ case SFM_READ :
+ if (psf->fileoffset > 0 && psf->filelength > 0)
+ filelen = psf->filelength ;
+ break ;
+
+ case SFM_RDWR :
+ /*
+ ** Cannot open embedded files SFM_RDWR so we don't need to
+ ** subtract psf->fileoffset. We already have the answer we
+ ** need.
+ */
+ break ;
+
+ default :
+ /* Shouldn't be here, so return error. */
+ filelen = -1 ;
+ } ;
+
+ return filelen ;
+} /* psf_get_filelen */
+
+/* USE_WINDOWS_API */ void
+psf_init_files (SF_PRIVATE *psf)
+{ psf->file.handle = NULL ;
+ psf->rsrc.handle = NULL ;
+ psf->file.hsaved = NULL ;
+} /* psf_init_files */
+
+/* USE_WINDOWS_API */ void
+psf_use_rsrc (SF_PRIVATE *psf, int on_off)
+{
+ if (on_off)
+ { if (psf->file.handle != psf->rsrc.handle)
+ { psf->file.hsaved = psf->file.handle ;
+ psf->file.handle = psf->rsrc.handle ;
+ } ;
+ }
+ else if (psf->file.handle == psf->rsrc.handle)
+ psf->file.handle = psf->file.hsaved ;
+
+ return ;
+} /* psf_use_rsrc */
+
+/* USE_WINDOWS_API */ static HANDLE
+psf_open_handle (PSF_FILE * pfile)
+{ DWORD dwDesiredAccess ;
+ DWORD dwShareMode ;
+ DWORD dwCreationDistribution ;
+ HANDLE handle ;
+
+ switch (pfile->mode)
+ { case SFM_READ :
+ dwDesiredAccess = GENERIC_READ ;
+ dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE ;
+ dwCreationDistribution = OPEN_EXISTING ;
+ break ;
+
+ case SFM_WRITE :
+ dwDesiredAccess = GENERIC_WRITE ;
+ dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE ;
+ dwCreationDistribution = CREATE_ALWAYS ;
+ break ;
+
+ case SFM_RDWR :
+ dwDesiredAccess = GENERIC_READ | GENERIC_WRITE ;
+ dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE ;
+ dwCreationDistribution = OPEN_ALWAYS ;
+ break ;
+
+ default :
+ return NULL ;
+ } ;
+
+ if (pfile->use_wchar)
+ handle = CreateFileW (
+ pfile->path.wc, /* pointer to name of the file */
+ dwDesiredAccess, /* access (read-write) mode */
+ dwShareMode, /* share mode */
+ 0, /* pointer to security attributes */
+ dwCreationDistribution, /* how to create */
+ FILE_ATTRIBUTE_NORMAL, /* file attributes (could use FILE_FLAG_SEQUENTIAL_SCAN) */
+ NULL /* handle to file with attributes to copy */
+ ) ;
+ else
+ handle = CreateFile (
+ pfile->path.c, /* pointer to name of the file */
+ dwDesiredAccess, /* access (read-write) mode */
+ dwShareMode, /* share mode */
+ 0, /* pointer to security attributes */
+ dwCreationDistribution, /* how to create */
+ FILE_ATTRIBUTE_NORMAL, /* file attributes (could use FILE_FLAG_SEQUENTIAL_SCAN) */
+ NULL /* handle to file with attributes to copy */
+ ) ;
+
+ if (handle == INVALID_HANDLE_VALUE)
+ return NULL ;
+
+ return handle ;
+} /* psf_open_handle */
+
+/* USE_WINDOWS_API */ static void
+psf_log_syserr (SF_PRIVATE *psf, int error)
+{ LPVOID lpMsgBuf ;
+
+ /* Only log an error if no error has been set yet. */
+ if (psf->error == 0)
+ { psf->error = SFE_SYSTEM ;
+
+ FormatMessage (
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,
+ error,
+ MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR) &lpMsgBuf,
+ 0,
+ NULL
+ ) ;
+
+ snprintf (psf->syserr, sizeof (psf->syserr), "System error : %s", (char*) lpMsgBuf) ;
+ LocalFree (lpMsgBuf) ;
+ } ;
+
+ return ;
+} /* psf_log_syserr */
+
+
+/* USE_WINDOWS_API */ int
+psf_close_rsrc (SF_PRIVATE *psf)
+{ psf_close_handle (psf->rsrc.handle) ;
+ psf->rsrc.handle = NULL ;
+ return 0 ;
+} /* psf_close_rsrc */
+
+
+/* USE_WINDOWS_API */ int
+psf_set_stdio (SF_PRIVATE *psf)
+{ HANDLE handle = NULL ;
+ int error = 0 ;
+
+ switch (psf->file.mode)
+ { case SFM_RDWR :
+ error = SFE_OPEN_PIPE_RDWR ;
+ break ;
+
+ case SFM_READ :
+ handle = GetStdHandle (STD_INPUT_HANDLE) ;
+ psf->file.do_not_close_descriptor = 1 ;
+ break ;
+
+ case SFM_WRITE :
+ handle = GetStdHandle (STD_OUTPUT_HANDLE) ;
+ psf->file.do_not_close_descriptor = 1 ;
+ break ;
+
+ default :
+ error = SFE_BAD_OPEN_MODE ;
+ break ;
+ } ;
+
+ psf->file.handle = handle ;
+ psf->filelength = 0 ;
+
+ return error ;
+} /* psf_set_stdio */
+
+/* USE_WINDOWS_API */ void
+psf_set_file (SF_PRIVATE *psf, int fd)
+{ HANDLE handle ;
+ intptr_t osfhandle ;
+
+ osfhandle = _get_osfhandle (fd) ;
+ handle = (HANDLE) osfhandle ;
+
+ psf->file.handle = handle ;
+} /* psf_set_file */
+
+/* USE_WINDOWS_API */ int
+psf_file_valid (SF_PRIVATE *psf)
+{ if (psf->file.handle == NULL)
+ return SF_FALSE ;
+ if (psf->file.handle == INVALID_HANDLE_VALUE)
+ return SF_FALSE ;
+ return SF_TRUE ;
+} /* psf_set_file */
+
+/* USE_WINDOWS_API */ sf_count_t
+psf_fseek (SF_PRIVATE *psf, sf_count_t offset, int whence)
+{ sf_count_t new_position ;
+ LONG lDistanceToMove, lDistanceToMoveHigh ;
+ DWORD dwMoveMethod ;
+ DWORD dwResult, dwError ;
+
+ if (psf->virtual_io)
+ return psf->vio.seek (offset, whence, psf->vio_user_data) ;
+
+ switch (whence)
+ { case SEEK_SET :
+ offset += psf->fileoffset ;
+ dwMoveMethod = FILE_BEGIN ;
+ break ;
+
+ case SEEK_END :
+ dwMoveMethod = FILE_END ;
+ break ;
+
+ default :
+ dwMoveMethod = FILE_CURRENT ;
+ break ;
+ } ;
+
+ lDistanceToMove = (DWORD) (offset & 0xFFFFFFFF) ;
+ lDistanceToMoveHigh = (DWORD) ((offset >> 32) & 0xFFFFFFFF) ;
+
+ dwResult = SetFilePointer (psf->file.handle, lDistanceToMove, &lDistanceToMoveHigh, dwMoveMethod) ;
+
+ if (dwResult == 0xFFFFFFFF)
+ dwError = GetLastError () ;
+ else
+ dwError = NO_ERROR ;
+
+ if (dwError != NO_ERROR)
+ { psf_log_syserr (psf, dwError) ;
+ return -1 ;
+ } ;
+
+ new_position = (dwResult + ((__int64) lDistanceToMoveHigh << 32)) - psf->fileoffset ;
+
+ return new_position ;
+} /* psf_fseek */
+
+/* USE_WINDOWS_API */ sf_count_t
+psf_fread (void *ptr, sf_count_t bytes, sf_count_t items, SF_PRIVATE *psf)
+{ sf_count_t total = 0 ;
+ ssize_t count ;
+ DWORD dwNumberOfBytesRead ;
+
+ if (psf->virtual_io)
+ return psf->vio.read (ptr, bytes*items, psf->vio_user_data) / bytes ;
+
+ items *= bytes ;
+
+ /* Do this check after the multiplication above. */
+ if (items <= 0)
+ return 0 ;
+
+ while (items > 0)
+ { /* Break the writes down to a sensible size. */
+ count = (items > SENSIBLE_SIZE) ? SENSIBLE_SIZE : (ssize_t) items ;
+
+ if (ReadFile (psf->file.handle, ((char*) ptr) + total, count, &dwNumberOfBytesRead, 0) == 0)
+ { psf_log_syserr (psf, GetLastError ()) ;
+ break ;
+ }
+ else
+ count = dwNumberOfBytesRead ;
+
+ if (count == 0)
+ break ;
+
+ total += count ;
+ items -= count ;
+ } ;
+
+ if (psf->is_pipe)
+ psf->pipeoffset += total ;
+
+ return total / bytes ;
+} /* psf_fread */
+
+/* USE_WINDOWS_API */ sf_count_t
+psf_fwrite (const void *ptr, sf_count_t bytes, sf_count_t items, SF_PRIVATE *psf)
+{ sf_count_t total = 0 ;
+ ssize_t count ;
+ DWORD dwNumberOfBytesWritten ;
+
+ if (psf->virtual_io)
+ return psf->vio.write (ptr, bytes * items, psf->vio_user_data) / bytes ;
+
+ items *= bytes ;
+
+ /* Do this check after the multiplication above. */
+ if (items <= 0)
+ return 0 ;
+
+ while (items > 0)
+ { /* Break the writes down to a sensible size. */
+ count = (items > SENSIBLE_SIZE) ? SENSIBLE_SIZE : (ssize_t) items ;
+
+ if (WriteFile (psf->file.handle, ((const char*) ptr) + total, count, &dwNumberOfBytesWritten, 0) == 0)
+ { psf_log_syserr (psf, GetLastError ()) ;
+ break ;
+ }
+ else
+ count = dwNumberOfBytesWritten ;
+
+ if (count == 0)
+ break ;
+
+ total += count ;
+ items -= count ;
+ } ;
+
+ if (psf->is_pipe)
+ psf->pipeoffset += total ;
+
+ return total / bytes ;
+} /* psf_fwrite */
+
+/* USE_WINDOWS_API */ sf_count_t
+psf_ftell (SF_PRIVATE *psf)
+{ sf_count_t pos ;
+ LONG lDistanceToMoveLow, lDistanceToMoveHigh ;
+ DWORD dwResult, dwError ;
+
+ if (psf->virtual_io)
+ return psf->vio.tell (psf->vio_user_data) ;
+
+ if (psf->is_pipe)
+ return psf->pipeoffset ;
+
+ lDistanceToMoveLow = 0 ;
+ lDistanceToMoveHigh = 0 ;
+
+ dwResult = SetFilePointer (psf->file.handle, lDistanceToMoveLow, &lDistanceToMoveHigh, FILE_CURRENT) ;
+
+ if (dwResult == 0xFFFFFFFF)
+ dwError = GetLastError () ;
+ else
+ dwError = NO_ERROR ;
+
+ if (dwError != NO_ERROR)
+ { psf_log_syserr (psf, dwError) ;
+ return -1 ;
+ } ;
+
+ pos = (dwResult + ((__int64) lDistanceToMoveHigh << 32)) ;
+
+ return pos - psf->fileoffset ;
+} /* psf_ftell */
+
+/* USE_WINDOWS_API */ static int
+psf_close_handle (HANDLE handle)
+{ if (handle == NULL)
+ return 0 ;
+
+ if (CloseHandle (handle) == 0)
+ return -1 ;
+
+ return 0 ;
+} /* psf_close_handle */
+
+/* USE_WINDOWS_API */ sf_count_t
+psf_fgets (char *buffer, sf_count_t bufsize, SF_PRIVATE *psf)
+{ sf_count_t k = 0 ;
+ sf_count_t count ;
+ DWORD dwNumberOfBytesRead ;
+
+ while (k < bufsize - 1)
+ { if (ReadFile (psf->file.handle, &(buffer [k]), 1, &dwNumberOfBytesRead, 0) == 0)
+ { psf_log_syserr (psf, GetLastError ()) ;
+ break ;
+ }
+ else
+ { count = dwNumberOfBytesRead ;
+ /* note that we only check for '\n' not other line endings such as CRLF */
+ if (count == 0 || buffer [k++] == '\n')
+ break ;
+ } ;
+ } ;
+
+ buffer [k] = 0 ;
+
+ return k ;
+} /* psf_fgets */
+
+/* USE_WINDOWS_API */ int
+psf_is_pipe (SF_PRIVATE *psf)
+{
+ if (psf->virtual_io)
+ return SF_FALSE ;
+
+ if (GetFileType (psf->file.handle) == FILE_TYPE_DISK)
+ return SF_FALSE ;
+
+ /* Default to maximum safety. */
+ return SF_TRUE ;
+} /* psf_is_pipe */
+
+/* USE_WINDOWS_API */ sf_count_t
+psf_get_filelen_handle (HANDLE handle)
+{ sf_count_t filelen ;
+ DWORD dwFileSizeLow, dwFileSizeHigh, dwError = NO_ERROR ;
+
+ dwFileSizeLow = GetFileSize (handle, &dwFileSizeHigh) ;
+
+ if (dwFileSizeLow == 0xFFFFFFFF)
+ dwError = GetLastError () ;
+
+ if (dwError != NO_ERROR)
+ return (sf_count_t) -1 ;
+
+ filelen = dwFileSizeLow + ((__int64) dwFileSizeHigh << 32) ;
+
+ return filelen ;
+} /* psf_get_filelen_handle */
+
+/* USE_WINDOWS_API */ void
+psf_fsync (SF_PRIVATE *psf)
+{ FlushFileBuffers (psf->file.handle) ;
+} /* psf_fsync */
+
+
+/* USE_WINDOWS_API */ int
+psf_ftruncate (SF_PRIVATE *psf, sf_count_t len)
+{ int retval = 0 ;
+ LONG lDistanceToMoveLow, lDistanceToMoveHigh ;
+ DWORD dwResult, dwError = NO_ERROR ;
+
+ /* This implementation trashes the current file position.
+ ** should it save and restore it? what if the current position is past
+ ** the new end of file?
+ */
+
+ /* Returns 0 on success, non-zero on failure. */
+ if (len < 0)
+ return 1 ;
+
+ lDistanceToMoveLow = (DWORD) (len & 0xFFFFFFFF) ;
+ lDistanceToMoveHigh = (DWORD) ((len >> 32) & 0xFFFFFFFF) ;
+
+ dwResult = SetFilePointer (psf->file.handle, lDistanceToMoveLow, &lDistanceToMoveHigh, FILE_BEGIN) ;
+
+ if (dwResult == 0xFFFFFFFF)
+ dwError = GetLastError () ;
+
+ if (dwError != NO_ERROR)
+ { retval = -1 ;
+ psf_log_syserr (psf, dwError) ;
+ }
+ else
+ { /* Note: when SetEndOfFile is used to extend a file, the contents of the
+ ** new portion of the file is undefined. This is unlike chsize(),
+ ** which guarantees that the new portion of the file will be zeroed.
+ ** Not sure if this is important or not.
+ */
+ if (SetEndOfFile (psf->file.handle) == 0)
+ { retval = -1 ;
+ psf_log_syserr (psf, GetLastError ()) ;
+ } ;
+ } ;
+
+ return retval ;
+} /* psf_ftruncate */
+
+
+#else
+/* Win32 file i/o functions implemented using Unix-style file i/o API */
+
+/* Win32 has a 64 file offset seek function:
+**
+** __int64 _lseeki64 (int handle, __int64 offset, int origin) ;
+**
+** It also has a 64 bit fstat function:
+**
+** int fstati64 (int, struct _stati64) ;
+**
+** but the fscking thing doesn't work!!!!! The file size parameter returned
+** by this function is only valid up until more data is written at the end of
+** the file. That makes this function completely 100% useless.
+*/
+
+#include <io.h>
+#include <direct.h>
+
+/* Win32 */ int
+psf_fopen (SF_PRIVATE *psf, const char *pathname, int open_mode)
+{ int oflag, mode ;
+
+ switch (open_mode)
+ { case SFM_READ :
+ oflag = O_RDONLY | O_BINARY ;
+ mode = 0 ;
+ break ;
+
+ case SFM_WRITE :
+ oflag = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY ;
+ mode = S_IRUSR | S_IWUSR | S_IRGRP ;
+ break ;
+
+ case SFM_RDWR :
+ oflag = O_RDWR | O_CREAT | O_BINARY ;
+ mode = S_IRUSR | S_IWUSR | S_IRGRP ;
+ break ;
+
+ default :
+ psf->error = SFE_BAD_OPEN_MODE ;
+ return -1 ;
+ break ;
+ } ;
+
+ if (mode == 0)
+ psf->file.filedes = open (pathname, oflag) ;
+ else
+ psf->file.filedes = open (pathname, oflag, mode) ;
+
+ if (psf->file.filedes == -1)
+ psf_log_syserr (psf, errno) ;
+
+ return psf->file.filedes ;
+} /* psf_fopen */
+
+/* Win32 */ sf_count_t
+psf_fseek (SF_PRIVATE *psf, sf_count_t offset, int whence)
+{ sf_count_t new_position ;
+
+ if (psf->virtual_io)
+ return psf->vio.seek (offset, whence, psf->vio_user_data) ;
+
+ switch (whence)
+ { case SEEK_SET :
+ offset += psf->fileoffset ;
+ break ;
+
+ case SEEK_END :
+ if (psf->file.mode == SFM_WRITE)
+ { new_position = _lseeki64 (psf->file.filedes, offset, whence) ;
+
+ if (new_position < 0)
+ psf_log_syserr (psf, errno) ;
+
+ return new_position - psf->fileoffset ;
+ } ;
+
+ /* Transform SEEK_END into a SEEK_SET, ie find the file
+ ** length add the requested offset (should be <= 0) to
+ ** get the offset wrt the start of file.
+ */
+ whence = SEEK_SET ;
+ offset = _lseeki64 (psf->file.filedes, 0, SEEK_END) + offset ;
+ break ;
+
+ default :
+ /* No need to do anything about SEEK_CUR. */
+ break ;
+ } ;
+
+ /*
+ ** Bypass weird Win32-ism if necessary.
+ ** _lseeki64() returns an "invalid parameter" error if called with the
+ ** offset == 0 and whence == SEEK_CUR.
+ *** Use the _telli64() function instead.
+ */
+ if (offset == 0 && whence == SEEK_CUR)
+ new_position = _telli64 (psf->file.filedes) ;
+ else
+ new_position = _lseeki64 (psf->file.filedes, offset, whence) ;
+
+ if (new_position < 0)
+ psf_log_syserr (psf, errno) ;
+
+ new_position -= psf->fileoffset ;
+
+ return new_position ;
+} /* psf_fseek */
+
+/* Win32 */ sf_count_t
+psf_fread (void *ptr, sf_count_t bytes, sf_count_t items, SF_PRIVATE *psf)
+{ sf_count_t total = 0 ;
+ ssize_t count ;
+
+ if (psf->virtual_io)
+ return psf->vio.read (ptr, bytes*items, psf->vio_user_data) / bytes ;
+
+ items *= bytes ;
+
+ /* Do this check after the multiplication above. */
+ if (items <= 0)
+ return 0 ;
+
+ while (items > 0)
+ { /* Break the writes down to a sensible size. */
+ count = (items > SENSIBLE_SIZE) ? SENSIBLE_SIZE : (ssize_t) items ;
+
+ count = read (psf->file.filedes, ((char*) ptr) + total, (size_t) count) ;
+
+ if (count == -1)
+ { if (errno == EINTR)
+ continue ;
+
+ psf_log_syserr (psf, errno) ;
+ break ;
+ } ;
+
+ if (count == 0)
+ break ;
+
+ total += count ;
+ items -= count ;
+ } ;
+
+ return total / bytes ;
+} /* psf_fread */
+
+/* Win32 */ sf_count_t
+psf_fwrite (const void *ptr, sf_count_t bytes, sf_count_t items, SF_PRIVATE *psf)
+{ sf_count_t total = 0 ;
+ ssize_t count ;
+
+ if (psf->virtual_io)
+ return psf->vio.write (ptr, bytes*items, psf->vio_user_data) / bytes ;
+
+ items *= bytes ;
+
+ /* Do this check after the multiplication above. */
+ if (items <= 0)
+ return 0 ;
+
+ while (items > 0)
+ { /* Break the writes down to a sensible size. */
+ count = (items > SENSIBLE_SIZE) ? SENSIBLE_SIZE : items ;
+
+ count = write (psf->file.filedes, ((const char*) ptr) + total, count) ;
+
+ if (count == -1)
+ { if (errno == EINTR)
+ continue ;
+
+ psf_log_syserr (psf, errno) ;
+ break ;
+ } ;
+
+ if (count == 0)
+ break ;
+
+ total += count ;
+ items -= count ;
+ } ;
+
+ return total / bytes ;
+} /* psf_fwrite */
+
+/* Win32 */ sf_count_t
+psf_ftell (SF_PRIVATE *psf)
+{ sf_count_t pos ;
+
+ if (psf->virtual_io)
+ return psf->vio.tell (psf->vio_user_data) ;
+
+ pos = _telli64 (psf->file.filedes) ;
+
+ if (pos == ((sf_count_t) -1))
+ { psf_log_syserr (psf, errno) ;
+ return -1 ;
+ } ;
+
+ return pos - psf->fileoffset ;
+} /* psf_ftell */
+
+/* Win32 */ int
+psf_fclose (SF_PRIVATE *psf)
+{ int retval ;
+
+ while ((retval = close (psf->file.filedes)) == -1 && errno == EINTR)
+ /* Do nothing. */ ;
+
+ if (retval == -1)
+ psf_log_syserr (psf, errno) ;
+
+ psf->file.filedes = -1 ;
+
+ return retval ;
+} /* psf_fclose */
+
+/* Win32 */ sf_count_t
+psf_fgets (char *buffer, sf_count_t bufsize, SF_PRIVATE *psf)
+{ sf_count_t k = 0 ;
+ sf_count_t count ;
+
+ while (k < bufsize - 1)
+ { count = read (psf->file.filedes, &(buffer [k]), 1) ;
+
+ if (count == -1)
+ { if (errno == EINTR)
+ continue ;
+
+ psf_log_syserr (psf, errno) ;
+ break ;
+ } ;
+
+ if (count == 0 || buffer [k++] == '\n')
+ break ;
+ } ;
+
+ buffer [k] = 0 ;
+
+ return k ;
+} /* psf_fgets */
+
+/* Win32 */ int
+psf_is_pipe (SF_PRIVATE *psf)
+{ struct stat statbuf ;
+
+ if (psf->virtual_io)
+ return SF_FALSE ;
+
+ /* Not sure if this works. */
+ if (fstat (psf->file.filedes, &statbuf) == -1)
+ { psf_log_syserr (psf, errno) ;
+ /* Default to maximum safety. */
+ return SF_TRUE ;
+ } ;
+
+ /* These macros are defined in Win32/unistd.h. */
+ if (S_ISFIFO (statbuf.st_mode) || S_ISSOCK (statbuf.st_mode))
+ return SF_TRUE ;
+
+ return SF_FALSE ;
+} /* psf_checkpipe */
+
+/* Win32 */ sf_count_t
+psf_get_filelen (SF_PRIVATE *psf)
+{
+#if 0
+ /*
+ ** Windoze is SOOOOO FUCKED!!!!!!!
+ ** This code should work but doesn't. Why?
+ ** Code below does work.
+ */
+ struct _stati64 statbuf ;
+
+ if (_fstati64 (psf->file.filedes, &statbuf))
+ { psf_log_syserr (psf, errno) ;
+ return (sf_count_t) -1 ;
+ } ;
+
+ return statbuf.st_size ;
+#else
+ sf_count_t current, filelen ;
+
+ if (psf->virtual_io)
+ return psf->vio.get_filelen (psf->vio_user_data) ;
+
+ if ((current = _telli64 (psf->file.filedes)) < 0)
+ { psf_log_syserr (psf, errno) ;
+ return (sf_count_t) -1 ;
+ } ;
+
+ /*
+ ** Lets face it, windoze if FUBAR!!!
+ **
+ ** For some reason, I have to call _lseeki64() TWICE to get to the
+ ** end of the file.
+ **
+ ** This might have been avoided if windows had implemented the POSIX
+ ** standard function fsync() but NO, that would have been too easy.
+ **
+ ** I am VERY close to saying that windoze will no longer be supported
+ ** by libsndfile and changing the license to GPL at the same time.
+ */
+
+ _lseeki64 (psf->file.filedes, 0, SEEK_END) ;
+
+ if ((filelen = _lseeki64 (psf->file.filedes, 0, SEEK_END)) < 0)
+ { psf_log_syserr (psf, errno) ;
+ return (sf_count_t) -1 ;
+ } ;
+
+ if (filelen > current)
+ _lseeki64 (psf->file.filedes, current, SEEK_SET) ;
+
+ switch (psf->file.mode)
+ { case SFM_WRITE :
+ filelen = filelen - psf->fileoffset ;
+ break ;
+
+ case SFM_READ :
+ if (psf->fileoffset > 0 && psf->filelength > 0)
+ filelen = psf->filelength ;
+ break ;
+
+ case SFM_RDWR :
+ /*
+ ** Cannot open embedded files SFM_RDWR so we don't need to
+ ** subtract psf->fileoffset. We already have the answer we
+ ** need.
+ */
+ break ;
+
+ default :
+ filelen = 0 ;
+ } ;
+
+ return filelen ;
+#endif
+} /* psf_get_filelen */
+
+/* Win32 */ int
+psf_ftruncate (SF_PRIVATE *psf, sf_count_t len)
+{ int retval ;
+
+ /* Returns 0 on success, non-zero on failure. */
+ if (len < 0)
+ return 1 ;
+
+ /* The global village idiots at micorsoft decided to implement
+ ** nearly all the required 64 bit file offset functions except
+ ** for one, truncate. The fscking morons!
+ **
+ ** This is not 64 bit file offset clean. Somone needs to clean
+ ** this up.
+ */
+ if (len > 0x7FFFFFFF)
+ return -1 ;
+
+ retval = chsize (psf->file.filedes, len) ;
+
+ if (retval == -1)
+ psf_log_syserr (psf, errno) ;
+
+ return retval ;
+} /* psf_ftruncate */
+
+
+static void
+psf_log_syserr (SF_PRIVATE *psf, int error)
+{
+ /* Only log an error if no error has been set yet. */
+ if (psf->error == 0)
+ { psf->error = SFE_SYSTEM ;
+ snprintf (psf->syserr, sizeof (psf->syserr), "System error : %s", strerror (error)) ;
+ } ;
+
+ return ;
+} /* psf_log_syserr */
+
+#endif
+