xref: /third_party/libsnd/src/file_io.c (revision b815c7f3)
1/*
2** Copyright (C) 2002-2014 Erik de Castro Lopo <erikd@mega-nerd.com>
3** Copyright (C) 2003 Ross Bencina <rbencina@iprimus.com.au>
4**
5** This program is free software; you can redistribute it and/or modify
6** it under the terms of the GNU Lesser General Public License as published by
7** the Free Software Foundation; either version 2.1 of the License, or
8** (at your option) any later version.
9**
10** This program is distributed in the hope that it will be useful,
11** but WITHOUT ANY WARRANTY; without even the implied warranty of
12** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13** GNU Lesser General Public License for more details.
14**
15** You should have received a copy of the GNU Lesser General Public License
16** along with this program; if not, write to the Free Software
17** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18*/
19
20/*
21**	The file is split into three sections as follows:
22**		- The top section (USE_WINDOWS_API == 0) for Linux, Unix and MacOSX
23**			systems (including Cygwin).
24**		- The middle section (USE_WINDOWS_API == 1) for microsoft windows
25**			(including MinGW) using the native windows API.
26**		- A legacy windows section which attempted to work around grevious
27**			bugs in microsoft's POSIX implementation.
28*/
29
30/*
31**	The header file sfconfig.h MUST be included before the others to ensure
32**	that large file support is enabled correctly on Unix systems.
33*/
34
35#include "sfconfig.h"
36
37#if USE_WINDOWS_API
38
39/* Don't include rarely used headers, speed up build */
40#define WIN32_LEAN_AND_MEAN
41
42#include <windows.h>
43#endif
44
45#include <stdio.h>
46#include <stdlib.h>
47
48#if HAVE_UNISTD_H
49#include <unistd.h>
50#else
51#include <io.h>
52#endif
53
54#if (HAVE_DECL_S_IRGRP == 0)
55#include <sf_unistd.h>
56#endif
57
58#include <string.h>
59#include <fcntl.h>
60#include <errno.h>
61#include <sys/stat.h>
62
63#include "sndfile.h"
64#include "common.h"
65
66#define	SENSIBLE_SIZE	(0x40000000)
67
68/*
69**	Neat solution to the Win32/OS2 binary file flage requirement.
70**	If O_BINARY isn't already defined by the inclusion of the system
71**	headers, set it to zero.
72*/
73#ifndef O_BINARY
74#define O_BINARY 0
75#endif
76
77static void psf_log_syserr (SF_PRIVATE *psf, int error) ;
78
79int
80psf_copy_filename (SF_PRIVATE *psf, const char *path)
81{	const char *ccptr ;
82	char *cptr ;
83
84	if (strlen (path) > 1 && strlen (path) - 1 >= sizeof (psf->file.path))
85	{	psf->error = SFE_FILENAME_TOO_LONG ;
86		return psf->error ;
87		} ;
88
89	snprintf (psf->file.path, sizeof (psf->file.path), "%s", path) ;
90	if ((ccptr = strrchr (path, '/')) || (ccptr = strrchr (path, '\\')))
91		ccptr ++ ;
92	else
93		ccptr = path ;
94
95	snprintf (psf->file.name, sizeof (psf->file.name), "%s", ccptr) ;
96
97	/* Now grab the directory. */
98	snprintf (psf->file.dir, sizeof (psf->file.dir), "%s", path) ;
99	if ((cptr = strrchr (psf->file.dir, '/')) || (cptr = strrchr (psf->file.dir, '\\')))
100		cptr [1] = 0 ;
101	else
102		psf->file.dir [0] = 0 ;
103
104	return 0 ;
105} /* psf_copy_filename */
106
107#if (USE_WINDOWS_API == 0)
108
109/*------------------------------------------------------------------------------
110** Win32 stuff at the bottom of the file. Unix and other sensible OSes here.
111*/
112
113static int psf_close_fd (int fd) ;
114static int psf_open_fd (PSF_FILE * pfile) ;
115static sf_count_t psf_get_filelen_fd (int fd) ;
116
117int
118psf_fopen (SF_PRIVATE *psf)
119{
120	psf->error = 0 ;
121	psf->file.filedes = psf_open_fd (&psf->file) ;
122
123	if (psf->file.filedes == - SFE_BAD_OPEN_MODE)
124	{	psf->error = SFE_BAD_OPEN_MODE ;
125		psf->file.filedes = -1 ;
126		return psf->error ;
127		} ;
128
129	if (psf->file.filedes == -1)
130		psf_log_syserr (psf, errno) ;
131
132	return psf->error ;
133} /* psf_fopen */
134
135int
136psf_fclose (SF_PRIVATE *psf)
137{	int retval ;
138
139	if (psf->virtual_io)
140		return 0 ;
141
142	if (psf->file.do_not_close_descriptor)
143	{	psf->file.filedes = -1 ;
144		return 0 ;
145		} ;
146
147	if ((retval = psf_close_fd (psf->file.filedes)) == -1)
148		psf_log_syserr (psf, errno) ;
149
150	psf->file.filedes = -1 ;
151
152	return retval ;
153} /* psf_fclose */
154
155int
156psf_open_rsrc (SF_PRIVATE *psf)
157{	size_t count ;
158
159	if (psf->rsrc.filedes > 0)
160		return 0 ;
161
162	/* Test for MacOSX style resource fork on HPFS or HPFS+ filesystems. */
163	count = snprintf (psf->rsrc.path, sizeof (psf->rsrc.path), "%s/..namedfork/rsrc", psf->file.path) ;
164	psf->error = SFE_NO_ERROR ;
165	if (count < sizeof (psf->rsrc.path))
166	{	if ((psf->rsrc.filedes = psf_open_fd (&psf->rsrc)) >= 0)
167		{	psf->rsrclength = psf_get_filelen_fd (psf->rsrc.filedes) ;
168			if (psf->rsrclength > 0 || (psf->rsrc.mode & SFM_WRITE))
169				return SFE_NO_ERROR ;
170			psf_close_fd (psf->rsrc.filedes) ;
171			psf->rsrc.filedes = -1 ;
172			} ;
173
174		if (psf->rsrc.filedes == - SFE_BAD_OPEN_MODE)
175		{	psf->error = SFE_BAD_OPEN_MODE ;
176			return psf->error ;
177			} ;
178		} ;
179
180	/*
181	** Now try for a resource fork stored as a separate file in the same
182	** directory, but preceded with a dot underscore.
183	*/
184	count = snprintf (psf->rsrc.path, sizeof (psf->rsrc.path), "%s._%s", psf->file.dir, psf->file.name) ;
185	psf->error = SFE_NO_ERROR ;
186	if (count < sizeof (psf->rsrc.path) && (psf->rsrc.filedes = psf_open_fd (&psf->rsrc)) >= 0)
187	{	psf->rsrclength = psf_get_filelen_fd (psf->rsrc.filedes) ;
188		return SFE_NO_ERROR ;
189		} ;
190
191	/*
192	** Now try for a resource fork stored in a separate file in the
193	** .AppleDouble/ directory.
194	*/
195	count = snprintf (psf->rsrc.path, sizeof (psf->rsrc.path), "%s.AppleDouble/%s", psf->file.dir, psf->file.name) ;
196	psf->error = SFE_NO_ERROR ;
197	if (count < sizeof (psf->rsrc.path))
198	{	if ((psf->rsrc.filedes = psf_open_fd (&psf->rsrc)) >= 0)
199		{	psf->rsrclength = psf_get_filelen_fd (psf->rsrc.filedes) ;
200			return SFE_NO_ERROR ;
201			} ;
202
203		/* No resource file found. */
204		if (psf->rsrc.filedes == -1)
205			psf_log_syserr (psf, errno) ;
206		}
207	else
208	{	psf->error = SFE_OPEN_FAILED ;
209		} ;
210
211	psf->rsrc.filedes = -1 ;
212
213	return psf->error ;
214} /* psf_open_rsrc */
215
216sf_count_t
217psf_get_filelen (SF_PRIVATE *psf)
218{	sf_count_t	filelen ;
219
220	if (psf->virtual_io)
221		return psf->vio.get_filelen (psf->vio_user_data) ;
222
223	filelen = psf_get_filelen_fd (psf->file.filedes) ;
224
225	if (filelen == -1)
226	{	psf_log_syserr (psf, errno) ;
227		return (sf_count_t) -1 ;
228		} ;
229
230	if (filelen == -SFE_BAD_STAT_SIZE)
231	{	psf->error = SFE_BAD_STAT_SIZE ;
232		return (sf_count_t) -1 ;
233		} ;
234
235	switch (psf->file.mode)
236	{	case SFM_WRITE :
237			filelen = filelen - psf->fileoffset ;
238			break ;
239
240		case SFM_READ :
241			if (psf->fileoffset > 0 && psf->filelength > 0)
242				filelen = psf->filelength ;
243			break ;
244
245		case SFM_RDWR :
246			/*
247			** Cannot open embedded files SFM_RDWR so we don't need to
248			** subtract psf->fileoffset. We already have the answer we
249			** need.
250			*/
251			break ;
252
253		default :
254			/* Shouldn't be here, so return error. */
255			filelen = -1 ;
256		} ;
257
258	return filelen ;
259} /* psf_get_filelen */
260
261int
262psf_close_rsrc (SF_PRIVATE *psf)
263{	psf_close_fd (psf->rsrc.filedes) ;
264	psf->rsrc.filedes = -1 ;
265	return 0 ;
266} /* psf_close_rsrc */
267
268int
269psf_set_stdio (SF_PRIVATE *psf)
270{	int	error = 0 ;
271
272	switch (psf->file.mode)
273	{	case SFM_RDWR :
274				error = SFE_OPEN_PIPE_RDWR ;
275				break ;
276
277		case SFM_READ :
278				psf->file.filedes = 0 ;
279				break ;
280
281		case SFM_WRITE :
282				psf->file.filedes = 1 ;
283				break ;
284
285		default :
286				error = SFE_BAD_OPEN_MODE ;
287				break ;
288		} ;
289	psf->filelength = 0 ;
290
291	return error ;
292} /* psf_set_stdio */
293
294void
295psf_set_file (SF_PRIVATE *psf, int fd)
296{	psf->file.filedes = fd ;
297} /* psf_set_file */
298
299int
300psf_file_valid (SF_PRIVATE *psf)
301{	return (psf->file.filedes >= 0) ? SF_TRUE : SF_FALSE ;
302} /* psf_set_file */
303
304sf_count_t
305psf_fseek (SF_PRIVATE *psf, sf_count_t offset, int whence)
306{	sf_count_t	absolute_position ;
307
308	if (psf->virtual_io)
309		return psf->vio.seek (offset, whence, psf->vio_user_data) ;
310
311	/* When decoding from pipes sometimes see seeks to the pipeoffset, which appears to mean do nothing. */
312	if (psf->is_pipe)
313	{	if (whence != SEEK_SET || offset != psf->pipeoffset)
314			psf_log_printf (psf, "psf_fseek : pipe seek to value other than pipeoffset\n") ;
315		return offset ;
316		}
317
318	switch (whence)
319	{	case SEEK_SET :
320				offset += psf->fileoffset ;
321				break ;
322
323		case SEEK_END :
324				break ;
325
326		case SEEK_CUR :
327				break ;
328
329		default :
330				/* We really should not be here. */
331				psf_log_printf (psf, "psf_fseek : whence is %d *****.\n", whence) ;
332				return 0 ;
333		} ;
334
335	absolute_position = lseek (psf->file.filedes, offset, whence) ;
336
337	if (absolute_position < 0)
338		psf_log_syserr (psf, errno) ;
339
340	return absolute_position - psf->fileoffset ;
341} /* psf_fseek */
342
343sf_count_t
344psf_fread (void *ptr, sf_count_t bytes, sf_count_t items, SF_PRIVATE *psf)
345{	sf_count_t total = 0 ;
346	ssize_t	count ;
347
348	if (psf->virtual_io)
349		return psf->vio.read (ptr, bytes*items, psf->vio_user_data) / bytes ;
350
351	items *= bytes ;
352
353	/* Do this check after the multiplication above. */
354	if (items <= 0)
355		return 0 ;
356
357	while (items > 0)
358	{	/* Break the read down to a sensible size. */
359		count = (items > SENSIBLE_SIZE) ? SENSIBLE_SIZE : (ssize_t) items ;
360
361		count = read (psf->file.filedes, ((char*) ptr) + total, (size_t) count) ;
362
363		if (count == -1)
364		{	if (errno == EINTR)
365				continue ;
366
367			psf_log_syserr (psf, errno) ;
368			break ;
369			} ;
370
371		if (count == 0)
372			break ;
373
374		total += count ;
375		items -= count ;
376		} ;
377
378	if (psf->is_pipe)
379		psf->pipeoffset += total ;
380
381	return total / bytes ;
382} /* psf_fread */
383
384sf_count_t
385psf_fwrite (const void *ptr, sf_count_t bytes, sf_count_t items, SF_PRIVATE *psf)
386{	sf_count_t total = 0 ;
387	ssize_t	count ;
388
389	if (bytes == 0 || items == 0)
390		return 0 ;
391
392	if (psf->virtual_io)
393		return psf->vio.write (ptr, bytes*items, psf->vio_user_data) / bytes ;
394
395	items *= bytes ;
396
397	/* Do this check after the multiplication above. */
398	if (items <= 0)
399		return 0 ;
400
401	while (items > 0)
402	{	/* Break the writes down to a sensible size. */
403		count = (items > SENSIBLE_SIZE) ? SENSIBLE_SIZE : items ;
404
405		count = write (psf->file.filedes, ((const char*) ptr) + total, count) ;
406
407		if (count == -1)
408		{	if (errno == EINTR)
409				continue ;
410
411			psf_log_syserr (psf, errno) ;
412			break ;
413			} ;
414
415		if (count == 0)
416			break ;
417
418		total += count ;
419		items -= count ;
420		} ;
421
422	if (psf->is_pipe)
423		psf->pipeoffset += total ;
424
425	return total / bytes ;
426} /* psf_fwrite */
427
428sf_count_t
429psf_ftell (SF_PRIVATE *psf)
430{	sf_count_t pos ;
431
432	if (psf->virtual_io)
433		return psf->vio.tell (psf->vio_user_data) ;
434
435	if (psf->is_pipe)
436		return psf->pipeoffset ;
437
438	pos = lseek (psf->file.filedes, 0, SEEK_CUR) ;
439
440	if (pos == ((sf_count_t) -1))
441	{	psf_log_syserr (psf, errno) ;
442		return -1 ;
443		} ;
444
445	return pos - psf->fileoffset ;
446} /* psf_ftell */
447
448static int
449psf_close_fd (int fd)
450{	int retval ;
451
452	if (fd < 0)
453		return 0 ;
454
455	while ((retval = close (fd)) == -1 && errno == EINTR)
456		/* Do nothing. */ ;
457
458	return retval ;
459} /* psf_close_fd */
460
461sf_count_t
462psf_fgets (char *buffer, sf_count_t bufsize, SF_PRIVATE *psf)
463{	sf_count_t	k = 0 ;
464	sf_count_t		count ;
465
466	while (k < bufsize - 1)
467	{	count = read (psf->file.filedes, &(buffer [k]), 1) ;
468
469		if (count == -1)
470		{	if (errno == EINTR)
471				continue ;
472
473			psf_log_syserr (psf, errno) ;
474			break ;
475			} ;
476
477		if (count == 0 || buffer [k++] == '\n')
478			break ;
479		} ;
480
481	buffer [k] = 0 ;
482
483	return k ;
484} /* psf_fgets */
485
486int
487psf_is_pipe (SF_PRIVATE *psf)
488{	struct stat statbuf ;
489
490	if (psf->virtual_io)
491		return SF_FALSE ;
492
493	if (fstat (psf->file.filedes, &statbuf) == -1)
494	{	psf_log_syserr (psf, errno) ;
495		/* Default to maximum safety. */
496		return SF_TRUE ;
497		} ;
498
499	if (S_ISFIFO (statbuf.st_mode) || S_ISSOCK (statbuf.st_mode))
500		return SF_TRUE ;
501
502	return SF_FALSE ;
503} /* psf_is_pipe */
504
505static sf_count_t
506psf_get_filelen_fd (int fd)
507{
508#if (SIZEOF_OFF_T == 4 && HAVE_FSTAT64)
509	struct stat64 statbuf ;
510
511	if (fstat64 (fd, &statbuf) == -1)
512		return (sf_count_t) -1 ;
513
514	return statbuf.st_size ;
515#else
516	struct stat statbuf ;
517
518	if (fstat (fd, &statbuf) == -1)
519		return (sf_count_t) -1 ;
520
521	return statbuf.st_size ;
522#endif
523} /* psf_get_filelen_fd */
524
525int
526psf_ftruncate (SF_PRIVATE *psf, sf_count_t len)
527{	int retval ;
528
529	/* Returns 0 on success, non-zero on failure. */
530	if (len < 0)
531		return -1 ;
532
533	if ((sizeof (off_t) < sizeof (sf_count_t)) && len > 0x7FFFFFFF)
534		return -1 ;
535
536	retval = ftruncate (psf->file.filedes, len) ;
537
538	if (retval == -1)
539		psf_log_syserr (psf, errno) ;
540
541	return retval ;
542} /* psf_ftruncate */
543
544void
545psf_init_files (SF_PRIVATE *psf)
546{	psf->file.filedes = -1 ;
547	psf->rsrc.filedes = -1 ;
548	psf->file.savedes = -1 ;
549} /* psf_init_files */
550
551void
552psf_use_rsrc (SF_PRIVATE *psf, int on_off)
553{
554	if (on_off)
555	{	if (psf->file.filedes != psf->rsrc.filedes)
556		{	psf->file.savedes = psf->file.filedes ;
557			psf->file.filedes = psf->rsrc.filedes ;
558			} ;
559		}
560	else if (psf->file.filedes == psf->rsrc.filedes)
561		psf->file.filedes = psf->file.savedes ;
562
563	return ;
564} /* psf_use_rsrc */
565
566static int
567psf_open_fd (PSF_FILE * pfile)
568{	int fd, oflag, mode ;
569
570	/*
571	** Sanity check. If everything is OK, this test and the printfs will
572	** be optimised out. This is meant to catch the problems caused by
573	** "sfconfig.h" being included after <stdio.h>.
574	*/
575	if (sizeof (sf_count_t) != 8)
576	{	puts ("\n\n*** Fatal error : sizeof (sf_count_t) != 8") ;
577		puts ("*** This means that libsndfile was not configured correctly.\n") ;
578		exit (1) ;
579		} ;
580
581	switch (pfile->mode)
582	{	case SFM_READ :
583				oflag = O_RDONLY | O_BINARY ;
584				mode = 0 ;
585				break ;
586
587		case SFM_WRITE :
588				oflag = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY ;
589				mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH ;
590				break ;
591
592		case SFM_RDWR :
593				oflag = O_RDWR | O_CREAT | O_BINARY ;
594				mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH ;
595				break ;
596
597		default :
598				return - SFE_BAD_OPEN_MODE ;
599				break ;
600		} ;
601
602	if (mode == 0)
603		fd = open (pfile->path, oflag) ;
604	else
605		fd = open (pfile->path, oflag, mode) ;
606
607	return fd ;
608} /* psf_open_fd */
609
610static void
611psf_log_syserr (SF_PRIVATE *psf, int error)
612{
613	/* Only log an error if no error has been set yet. */
614	if (psf->error == 0)
615	{	psf->error = SFE_SYSTEM ;
616		snprintf (psf->syserr, sizeof (psf->syserr), "System error : %s.", strerror (error)) ;
617		} ;
618
619	return ;
620} /* psf_log_syserr */
621
622void
623psf_fsync (SF_PRIVATE *psf)
624{
625#if HAVE_FSYNC
626	if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
627		fsync (psf->file.filedes) ;
628#else
629	psf = NULL ;
630#endif
631} /* psf_fsync */
632
633#else
634
635/* Win32 file i/o functions implemented using native Win32 API */
636
637#ifndef WINAPI_PARTITION_SYSTEM
638#define WINAPI_PARTITION_SYSTEM 0
639#endif
640
641static int psf_close_handle (HANDLE handle) ;
642static HANDLE psf_open_handle (PSF_FILE * pfile) ;
643static sf_count_t psf_get_filelen_handle (HANDLE handle) ;
644
645/* USE_WINDOWS_API */ int
646psf_fopen (SF_PRIVATE *psf)
647{
648	psf->error = 0 ;
649	psf->file.handle = psf_open_handle (&psf->file) ;
650
651	if (psf->file.handle == INVALID_HANDLE_VALUE)
652		psf_log_syserr (psf, GetLastError ()) ;
653
654	return psf->error ;
655} /* psf_fopen */
656
657/* USE_WINDOWS_API */ int
658psf_fclose (SF_PRIVATE *psf)
659{	int retval ;
660
661	if (psf->virtual_io)
662		return 0 ;
663
664	if (psf->file.do_not_close_descriptor)
665	{	psf->file.handle = INVALID_HANDLE_VALUE ;
666		return 0 ;
667		} ;
668
669	if ((retval = psf_close_handle (psf->file.handle)) == -1)
670		psf_log_syserr (psf, GetLastError ()) ;
671
672	psf->file.handle = INVALID_HANDLE_VALUE ;
673
674	return retval ;
675} /* psf_fclose */
676
677/* USE_WINDOWS_API */ int
678psf_open_rsrc (SF_PRIVATE *psf)
679{
680	if (psf->rsrc.handle != INVALID_HANDLE_VALUE)
681		return 0 ;
682
683	/* Test for MacOSX style resource fork on HPFS or HPFS+ filesystems. */
684	snprintf (psf->rsrc.path, sizeof (psf->rsrc.path), "%s/rsrc", psf->file.path) ;
685	psf->error = SFE_NO_ERROR ;
686	if ((psf->rsrc.handle = psf_open_handle (&psf->rsrc)) != INVALID_HANDLE_VALUE)
687	{	psf->rsrclength = psf_get_filelen_handle (psf->rsrc.handle) ;
688		return SFE_NO_ERROR ;
689		} ;
690
691	/*
692	** Now try for a resource fork stored as a separate file in the same
693	** directory, but preceded with a dot underscore.
694	*/
695	snprintf (psf->rsrc.path, sizeof (psf->rsrc.path), "%s._%s", psf->file.dir, psf->file.name) ;
696	psf->error = SFE_NO_ERROR ;
697	if ((psf->rsrc.handle = psf_open_handle (&psf->rsrc)) != INVALID_HANDLE_VALUE)
698	{	psf->rsrclength = psf_get_filelen_handle (psf->rsrc.handle) ;
699		return SFE_NO_ERROR ;
700		} ;
701
702	/*
703	** Now try for a resource fork stored in a separate file in the
704	** .AppleDouble/ directory.
705	*/
706	snprintf (psf->rsrc.path, sizeof (psf->rsrc.path), "%s.AppleDouble/%s", psf->file.dir, psf->file.name) ;
707	psf->error = SFE_NO_ERROR ;
708	if ((psf->rsrc.handle = psf_open_handle (&psf->rsrc)) != INVALID_HANDLE_VALUE)
709	{	psf->rsrclength = psf_get_filelen_handle (psf->rsrc.handle) ;
710		return SFE_NO_ERROR ;
711		} ;
712
713	/* No resource file found. */
714	if (psf->rsrc.handle == INVALID_HANDLE_VALUE)
715		psf_log_syserr (psf, GetLastError ()) ;
716
717	return psf->error ;
718} /* psf_open_rsrc */
719
720/* USE_WINDOWS_API */ sf_count_t
721psf_get_filelen (SF_PRIVATE *psf)
722{	sf_count_t	filelen ;
723
724	if (psf->virtual_io)
725		return psf->vio.get_filelen (psf->vio_user_data) ;
726
727	filelen = psf_get_filelen_handle (psf->file.handle) ;
728
729	if (filelen == -1)
730	{	psf_log_syserr (psf, errno) ;
731		return (sf_count_t) -1 ;
732		} ;
733
734	if (filelen == -SFE_BAD_STAT_SIZE)
735	{	psf->error = SFE_BAD_STAT_SIZE ;
736		return (sf_count_t) -1 ;
737		} ;
738
739	switch (psf->file.mode)
740	{	case SFM_WRITE :
741			filelen = filelen - psf->fileoffset ;
742			break ;
743
744		case SFM_READ :
745			if (psf->fileoffset > 0 && psf->filelength > 0)
746				filelen = psf->filelength ;
747			break ;
748
749		case SFM_RDWR :
750			/*
751			** Cannot open embedded files SFM_RDWR so we don't need to
752			** subtract psf->fileoffset. We already have the answer we
753			** need.
754			*/
755			break ;
756
757		default :
758			/* Shouldn't be here, so return error. */
759			filelen = -1 ;
760		} ;
761
762	return filelen ;
763} /* psf_get_filelen */
764
765/* USE_WINDOWS_API */ void
766psf_init_files (SF_PRIVATE *psf)
767{	psf->file.handle = INVALID_HANDLE_VALUE ;
768	psf->rsrc.handle = INVALID_HANDLE_VALUE ;
769	psf->file.hsaved = INVALID_HANDLE_VALUE ;
770} /* psf_init_files */
771
772/* USE_WINDOWS_API */ void
773psf_use_rsrc (SF_PRIVATE *psf, int on_off)
774{
775	if (on_off)
776	{	if (psf->file.handle != psf->rsrc.handle)
777		{	psf->file.hsaved = psf->file.handle ;
778			psf->file.handle = psf->rsrc.handle ;
779			} ;
780		}
781	else if (psf->file.handle == psf->rsrc.handle)
782		psf->file.handle = psf->file.hsaved ;
783
784	return ;
785} /* psf_use_rsrc */
786
787/* USE_WINDOWS_API */ static HANDLE
788psf_open_handle (PSF_FILE * pfile)
789{	DWORD dwDesiredAccess ;
790	DWORD dwShareMode ;
791	DWORD dwCreationDistribution ;
792	HANDLE handle ;
793	LPWSTR pwszPath = NULL ;
794
795	switch (pfile->mode)
796	{	case SFM_READ :
797				dwDesiredAccess = GENERIC_READ ;
798				dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE ;
799				dwCreationDistribution = OPEN_EXISTING ;
800				break ;
801
802		case SFM_WRITE :
803				dwDesiredAccess = GENERIC_WRITE ;
804				dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE ;
805				dwCreationDistribution = CREATE_ALWAYS ;
806				break ;
807
808		case SFM_RDWR :
809				dwDesiredAccess = GENERIC_READ | GENERIC_WRITE ;
810				dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE ;
811				dwCreationDistribution = OPEN_ALWAYS ;
812				break ;
813
814		default :
815				return INVALID_HANDLE_VALUE ;
816		} ;
817
818	int nResult = MultiByteToWideChar (CP_UTF8, 0, pfile->path, -1, NULL, 0) ;
819	pwszPath = malloc (nResult * sizeof (WCHAR)) ;
820	if (!pwszPath)
821		return INVALID_HANDLE_VALUE ;
822
823	int nResult2 = MultiByteToWideChar (CP_UTF8, 0, pfile->path, -1, pwszPath, nResult) ;
824	if (nResult != nResult2)
825	{	free (pwszPath) ;
826		return INVALID_HANDLE_VALUE ;
827		} ;
828
829#if defined (WINAPI_FAMILY_PARTITION) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
830	CREATEFILE2_EXTENDED_PARAMETERS cfParams = { 0 } ;
831	cfParams.dwSize = sizeof (CREATEFILE2_EXTENDED_PARAMETERS) ;
832	cfParams.dwFileAttributes = FILE_ATTRIBUTE_NORMAL ;
833
834	handle = CreateFile2 (pwszPath, dwDesiredAccess, dwShareMode, dwCreationDistribution, &cfParams) ;
835#else
836	handle = CreateFileW (
837				pwszPath,					/* pointer to name of the file */
838				dwDesiredAccess,			/* access (read-write) mode */
839				dwShareMode,				/* share mode */
840				0,							/* pointer to security attributes */
841				dwCreationDistribution,		/* how to create */
842				FILE_ATTRIBUTE_NORMAL,		/* file attributes (could use FILE_FLAG_SEQUENTIAL_SCAN) */
843				NULL						/* handle to file with attributes to copy */
844				) ;
845#endif
846	free (pwszPath) ;
847
848	return handle ;
849} /* psf_open_handle */
850
851/* USE_WINDOWS_API */ static void
852psf_log_syserr (SF_PRIVATE *psf, int error)
853{	LPVOID lpMsgBuf ;
854
855	/* Only log an error if no error has been set yet. */
856	if (psf->error == 0)
857	{	psf->error = SFE_SYSTEM ;
858
859		FormatMessage (
860			FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
861			NULL,
862			error,
863			MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
864			(LPTSTR) &lpMsgBuf,
865			0,
866			NULL
867			) ;
868
869		snprintf (psf->syserr, sizeof (psf->syserr), "System error : %s", (char*) lpMsgBuf) ;
870		LocalFree (lpMsgBuf) ;
871		} ;
872
873	return ;
874} /* psf_log_syserr */
875
876
877/* USE_WINDOWS_API */ int
878psf_close_rsrc (SF_PRIVATE *psf)
879{	psf_close_handle (psf->rsrc.handle) ;
880	psf->rsrc.handle = INVALID_HANDLE_VALUE ;
881	return 0 ;
882} /* psf_close_rsrc */
883
884
885/* USE_WINDOWS_API */ int
886psf_set_stdio (SF_PRIVATE *psf)
887{	HANDLE	handle = INVALID_HANDLE_VALUE ;
888	int	error = 0 ;
889
890	switch (psf->file.mode)
891	{	case SFM_RDWR :
892				error = SFE_OPEN_PIPE_RDWR ;
893				break ;
894
895		case SFM_READ :
896				handle = GetStdHandle (STD_INPUT_HANDLE) ;
897				psf->file.do_not_close_descriptor = 1 ;
898				break ;
899
900		case SFM_WRITE :
901				handle = GetStdHandle (STD_OUTPUT_HANDLE) ;
902				psf->file.do_not_close_descriptor = 1 ;
903				break ;
904
905		default :
906				error = SFE_BAD_OPEN_MODE ;
907				break ;
908		} ;
909
910	psf->file.handle = handle ;
911	psf->filelength = 0 ;
912
913	return error ;
914} /* psf_set_stdio */
915
916/* USE_WINDOWS_API */ void
917psf_set_file (SF_PRIVATE *psf, int fd)
918{	HANDLE handle ;
919	intptr_t osfhandle ;
920
921	osfhandle = _get_osfhandle (fd) ;
922	handle = (HANDLE) osfhandle ;
923
924	psf->file.handle = handle ;
925} /* psf_set_file */
926
927/* USE_WINDOWS_API */ int
928psf_file_valid (SF_PRIVATE *psf)
929{	if (psf->file.handle == INVALID_HANDLE_VALUE)
930		return SF_FALSE ;
931	return SF_TRUE ;
932} /* psf_set_file */
933
934/* USE_WINDOWS_API */ sf_count_t
935psf_fseek (SF_PRIVATE *psf, sf_count_t offset, int whence)
936{	sf_count_t new_position ;
937	LARGE_INTEGER liDistanceToMove, liNewFilePointer ;
938	DWORD dwMoveMethod ;
939	BOOL fResult ;
940	DWORD dwError ;
941
942	if (psf->virtual_io)
943		return psf->vio.seek (offset, whence, psf->vio_user_data) ;
944
945	switch (whence)
946	{	case SEEK_SET :
947				offset += psf->fileoffset ;
948				dwMoveMethod = FILE_BEGIN ;
949				break ;
950
951		case SEEK_END :
952				dwMoveMethod = FILE_END ;
953				break ;
954
955		default :
956				dwMoveMethod = FILE_CURRENT ;
957				break ;
958		} ;
959
960	liDistanceToMove.QuadPart = offset ;
961
962	fResult = SetFilePointerEx (psf->file.handle, liDistanceToMove, &liNewFilePointer, dwMoveMethod) ;
963
964	if (fResult == FALSE)
965		dwError = GetLastError () ;
966	else
967		dwError = NO_ERROR ;
968
969	if (dwError != NO_ERROR)
970	{	psf_log_syserr (psf, dwError) ;
971		return -1 ;
972		} ;
973
974	new_position = liNewFilePointer.QuadPart - psf->fileoffset ;
975
976	return new_position ;
977} /* psf_fseek */
978
979/* USE_WINDOWS_API */ sf_count_t
980psf_fread (void *ptr, sf_count_t bytes, sf_count_t items, SF_PRIVATE *psf)
981{	sf_count_t total = 0 ;
982	ssize_t count ;
983	DWORD dwNumberOfBytesRead ;
984
985	if (psf->virtual_io)
986		return psf->vio.read (ptr, bytes*items, psf->vio_user_data) / bytes ;
987
988	items *= bytes ;
989
990	/* Do this check after the multiplication above. */
991	if (items <= 0)
992		return 0 ;
993
994	while (items > 0)
995	{	/* Break the writes down to a sensible size. */
996		count = (items > SENSIBLE_SIZE) ? SENSIBLE_SIZE : (ssize_t) items ;
997
998		if (ReadFile (psf->file.handle, ((char*) ptr) + total, count, &dwNumberOfBytesRead, 0) == 0)
999		{	psf_log_syserr (psf, GetLastError ()) ;
1000			break ;
1001			}
1002		else
1003			count = dwNumberOfBytesRead ;
1004
1005		if (count == 0)
1006			break ;
1007
1008		total += count ;
1009		items -= count ;
1010		} ;
1011
1012	if (psf->is_pipe)
1013		psf->pipeoffset += total ;
1014
1015	return total / bytes ;
1016} /* psf_fread */
1017
1018/* USE_WINDOWS_API */ sf_count_t
1019psf_fwrite (const void *ptr, sf_count_t bytes, sf_count_t items, SF_PRIVATE *psf)
1020{	sf_count_t total = 0 ;
1021	ssize_t	count ;
1022	DWORD dwNumberOfBytesWritten ;
1023
1024	if (psf->virtual_io)
1025		return psf->vio.write (ptr, bytes * items, psf->vio_user_data) / bytes ;
1026
1027	items *= bytes ;
1028
1029	/* Do this check after the multiplication above. */
1030	if (items <= 0)
1031		return 0 ;
1032
1033	while (items > 0)
1034	{	/* Break the writes down to a sensible size. */
1035		count = (items > SENSIBLE_SIZE) ? SENSIBLE_SIZE : (ssize_t) items ;
1036
1037		if (WriteFile (psf->file.handle, ((const char*) ptr) + total, count, &dwNumberOfBytesWritten, 0) == 0)
1038		{	psf_log_syserr (psf, GetLastError ()) ;
1039			break ;
1040			}
1041		else
1042			count = dwNumberOfBytesWritten ;
1043
1044		if (count == 0)
1045			break ;
1046
1047		total += count ;
1048		items -= count ;
1049		} ;
1050
1051	if (psf->is_pipe)
1052		psf->pipeoffset += total ;
1053
1054	return total / bytes ;
1055} /* psf_fwrite */
1056
1057/* USE_WINDOWS_API */ sf_count_t
1058psf_ftell (SF_PRIVATE *psf)
1059{	sf_count_t pos ;
1060	LARGE_INTEGER liDistanceToMove, liNewFilePointer ;
1061	BOOL fResult ;
1062	DWORD dwError ;
1063
1064	if (psf->virtual_io)
1065		return psf->vio.tell (psf->vio_user_data) ;
1066
1067	if (psf->is_pipe)
1068		return psf->pipeoffset ;
1069
1070	liDistanceToMove.QuadPart = 0 ;
1071
1072	fResult = SetFilePointerEx (psf->file.handle, liDistanceToMove, &liNewFilePointer, FILE_CURRENT) ;
1073
1074	if (fResult == FALSE)
1075		dwError = GetLastError () ;
1076	else
1077		dwError = NO_ERROR ;
1078
1079	if (dwError != NO_ERROR)
1080	{	psf_log_syserr (psf, dwError) ;
1081		return -1 ;
1082		} ;
1083
1084	pos = liNewFilePointer.QuadPart ;
1085
1086	return pos - psf->fileoffset ;
1087} /* psf_ftell */
1088
1089/* USE_WINDOWS_API */ static int
1090psf_close_handle (HANDLE handle)
1091{	if (handle == INVALID_HANDLE_VALUE)
1092		return 0 ;
1093
1094	if (CloseHandle (handle) == 0)
1095		return -1 ;
1096
1097	return 0 ;
1098} /* psf_close_handle */
1099
1100/* USE_WINDOWS_API */ sf_count_t
1101psf_fgets (char *buffer, sf_count_t bufsize, SF_PRIVATE *psf)
1102{	sf_count_t k = 0 ;
1103	sf_count_t count ;
1104	DWORD dwNumberOfBytesRead ;
1105
1106	while (k < bufsize - 1)
1107	{	if (ReadFile (psf->file.handle, &(buffer [k]), 1, &dwNumberOfBytesRead, 0) == 0)
1108		{	psf_log_syserr (psf, GetLastError ()) ;
1109			break ;
1110			}
1111		else
1112		{	count = dwNumberOfBytesRead ;
1113			/* note that we only check for '\n' not other line endings such as CRLF */
1114			if (count == 0 || buffer [k++] == '\n')
1115				break ;
1116			} ;
1117		} ;
1118
1119	buffer [k] = 0 ;
1120
1121	return k ;
1122} /* psf_fgets */
1123
1124/* USE_WINDOWS_API */ int
1125psf_is_pipe (SF_PRIVATE *psf)
1126{
1127	if (psf->virtual_io)
1128		return SF_FALSE ;
1129
1130	if (GetFileType (psf->file.handle) == FILE_TYPE_DISK)
1131		return SF_FALSE ;
1132
1133	/* Default to maximum safety. */
1134	return SF_TRUE ;
1135} /* psf_is_pipe */
1136
1137/* USE_WINDOWS_API */ sf_count_t
1138psf_get_filelen_handle (HANDLE handle)
1139{	sf_count_t filelen ;
1140	LARGE_INTEGER liFileSize ;
1141	BOOL fResult ;
1142	DWORD dwError = NO_ERROR ;
1143
1144	fResult = GetFileSizeEx (handle, &liFileSize) ;
1145
1146	if (fResult == FALSE)
1147		dwError = GetLastError () ;
1148
1149	if (dwError != NO_ERROR)
1150		return (sf_count_t) -1 ;
1151
1152	filelen = liFileSize.QuadPart ;
1153
1154	return filelen ;
1155} /* psf_get_filelen_handle */
1156
1157/* USE_WINDOWS_API */ void
1158psf_fsync (SF_PRIVATE *psf)
1159{	FlushFileBuffers (psf->file.handle) ;
1160} /* psf_fsync */
1161
1162
1163/* USE_WINDOWS_API */ int
1164psf_ftruncate (SF_PRIVATE *psf, sf_count_t len)
1165{	int retval = 0 ;
1166	LARGE_INTEGER liDistanceToMove ;
1167	BOOL fResult ;
1168	DWORD dwError = NO_ERROR ;
1169
1170	/* This implementation trashes the current file position.
1171	** should it save and restore it? what if the current position is past
1172	** the new end of file?
1173	*/
1174
1175	/* Returns 0 on success, non-zero on failure. */
1176	if (len < 0)
1177		return 1 ;
1178
1179	liDistanceToMove.QuadPart = (sf_count_t) len ;
1180
1181	fResult = SetFilePointerEx (psf->file.handle, liDistanceToMove, NULL, FILE_BEGIN) ;
1182
1183	if (fResult == FALSE)
1184		dwError = GetLastError () ;
1185
1186	if (dwError != NO_ERROR)
1187	{	retval = -1 ;
1188		psf_log_syserr (psf, dwError) ;
1189		}
1190	else
1191	{	/* Note: when SetEndOfFile is used to extend a file, the contents of the
1192		** new portion of the file is undefined. This is unlike chsize(),
1193		** which guarantees that the new portion of the file will be zeroed.
1194		** Not sure if this is important or not.
1195		*/
1196		if (SetEndOfFile (psf->file.handle) == 0)
1197		{	retval = -1 ;
1198			psf_log_syserr (psf, GetLastError ()) ;
1199			} ;
1200		} ;
1201
1202	return retval ;
1203} /* psf_ftruncate */
1204
1205#endif
1206
1207