/* 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 #include #include #include #include #include #include #if HAVE_MMAP #include #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 ; } }