summaryrefslogtreecommitdiffstats
path: root/io-unix.c
diff options
context:
space:
mode:
Diffstat (limited to 'io-unix.c')
-rw-r--r--io-unix.c176
1 files changed, 176 insertions, 0 deletions
diff --git a/io-unix.c b/io-unix.c
new file mode 100644
index 0000000..d7c59ed
--- /dev/null
+++ b/io-unix.c
@@ -0,0 +1,176 @@
+/* OS-specific IO functions for xcftools
+ *
+ * Copyright (C) 2006 Henning Makholm
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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
+ */
+
+#include "xcftools.h"
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#if HAVE_MMAP
+#include <sys/mman.h>
+#endif
+
+static FILE *xcfstream = 0 ;
+
+void
+free_or_close_xcf(void)
+{
+ if( xcf_file ) {
+ if( xcfstream ) {
+ munmap(xcf_file,xcf_length) ;
+ fclose(xcfstream);
+ xcf_file = 0 ;
+ xcfstream = 0 ;
+ } else {
+ free(xcf_file) ;
+ xcf_file = 0 ;
+ }
+ }
+}
+
+void
+read_or_mmap_xcf(const char *filename,const char *unzipper)
+{
+ struct stat statbuf ;
+
+ free_or_close_xcf() ;
+
+ if( strcmp(filename,"-") != 0 ) {
+ if( access(filename,R_OK) != 0 )
+ FatalGeneric(21,"!%s",filename);
+ }
+
+ if( !unzipper ) {
+ const char *pc ;
+ pc = filename + strlen(filename) ;
+ if( pc-filename > 2 && strcmp(pc-2,"gz") == 0 )
+ unzipper = "zcat" ;
+ else if ( pc-filename > 3 && strcmp(pc-3,"bz2") == 0 )
+ unzipper = "bzcat" ;
+ else
+ unzipper = "" ;
+ } else if( strcmp(unzipper,"cat") == 0 )
+ unzipper = "" ;
+
+ if( *unzipper ) {
+ int pid, status, outfd ;
+#if HAVE_MMAP
+ xcfstream = tmpfile() ;
+ if( !xcfstream )
+ FatalUnexpected(_("!Cannot create temporary unzipped file"));
+ outfd = fileno(xcfstream) ;
+#else
+ int fh[2] ;
+ if( pipe(fh) < 0 )
+ FatalUnexpected(_("!Cannot create pipe for %s"),unzipper);
+ xcfstream = fdopen(fh[1],"rb") ;
+ if( !xcfstream )
+ FatalUnexpected(_("!Cannot fdopen() unzipper pipe"));
+ outfd = fh[0] ;
+#endif
+ if( (pid = fork()) == 0 ) {
+ /* We're the child */
+ if( dup2(outfd,1) < 0 ) {
+ perror(_("Cannot dup2 in unzip process"));
+ exit(127) ;
+ }
+ fclose(xcfstream) ;
+ execlp(unzipper,unzipper,filename,NULL) ;
+ fprintf(stderr,_("Cannot execute "));
+ perror(unzipper);
+ exit(126) ;
+ }
+#if HAVE_MMAP
+ while( wait(&status) != pid )
+ ;
+ if( WIFEXITED(status) ) {
+ status = WEXITSTATUS(status) ;
+ if( status > 0 ) {
+ fclose(xcfstream) ;
+ xcfstream = 0 ;
+ FatalGeneric(status,NULL);
+ }
+ } else {
+ fclose(xcfstream) ;
+ xcfstream = 0 ;
+ FatalGeneric(126,_("%s terminated abnormally"),unzipper);
+ }
+#else
+ close(fh[0]) ;
+#endif
+ } else if( strcmp(filename,"-") == 0 ) {
+ xcfstream = fdopen(dup(0),"rb") ;
+ if( !xcfstream )
+ FatalUnexpected(_("!Cannot dup stdin for input")) ;
+ } else {
+ xcfstream = fopen(filename,"rb") ;
+ if( !xcfstream )
+ FatalGeneric(21,_("!Cannot open %s"),filename);
+ }
+ /* OK, now we have an open stream ... */
+ if( fstat(fileno(xcfstream),&statbuf) == 0 &&
+ (statbuf.st_mode & S_IFMT) == S_IFREG ) {
+ xcf_length = statbuf.st_size ;
+#if HAVE_MMAP
+ xcf_file = mmap(0,xcf_length,PROT_READ,MAP_SHARED,fileno(xcfstream),0);
+ if( xcf_file != (void*)-1 )
+ return ;
+ if( errno != ENODEV ) {
+ int saved = errno ;
+ fclose(xcfstream) ;
+ xcf_file = 0 ;
+ errno = saved ;
+ FatalUnexpected(_("!Could not mmap input"));
+ }
+#endif
+ xcf_file = malloc(xcf_length);
+ if( xcf_file == 0 )
+ FatalUnexpected(_("Out of memory for xcf data"));
+ if( fread(xcf_file,1,xcf_length,xcfstream) != xcf_length ) {
+ if( feof(xcfstream) )
+ FatalUnexpected(_("XCF file shrunk while reading it"));
+ else
+ FatalUnexpected(_("!Could not read xcf data"));
+ }
+ fclose(xcfstream) ;
+ xcfstream = 0 ;
+ } else {
+ size_t blocksize = 0x80000 ; /* 512 KB */
+ xcf_length = 0 ;
+ xcf_file = 0 ;
+ while(1) {
+ xcf_file = realloc(xcf_file,blocksize) ;
+ if( xcf_file == 0 )
+ FatalUnexpected(_("Out of memory for xcf data"));
+ size_t actual = fread(xcf_file+xcf_length,1,blocksize-xcf_length,
+ xcfstream) ;
+ xcf_length += actual ;
+ if( feof(xcfstream) )
+ break ;
+ if( xcf_length < blocksize ) {
+ FatalUnexpected(_("Could not read xcf data")) ;
+ }
+ blocksize += (blocksize >> 1) & ~(size_t)0x3FFF ; /* 16 KB granularity */
+ }
+ fclose(xcfstream) ;
+ xcfstream = 0 ;
+ }
+}