1 /** \file znzlib.c
2     \brief Low level i/o interface to compressed and noncompressed files.
3         Written by Mark Jenkinson, FMRIB
4 
5 This library provides an interface to both compressed (gzip/zlib) and
6 uncompressed (normal) file IO.  The functions are written to have the
7 same interface as the standard file IO functions.
8 
9 To use this library instead of normal file IO, the following changes
10 are required:
11  - replace all instances of FILE* with znzFile
12  - change the name of all function calls, replacing the initial character
13    f with the znz  (e.g. fseek becomes znzseek)
14    one exception is rewind() -> znzrewind()
15  - add a third parameter to all calls to znzopen (previously fopen)
16    that specifies whether to use compression (1) or not (0)
17  - use znz_isnull rather than any (pointer == NULL) comparisons in the code
18    for znzfile types (normally done after a return from znzopen)
19 
20 NB: seeks for writable files with compression are quite restricted
21 
22  */
23 #ifdef ZNZ
24 #include <zlib.h>
25 #include "znzlib.h"
26 #include "sighandler.h"
27 /*
28 znzlib.c  (zipped or non-zipped library)
29 
30 *****            This code is released to the public domain.            *****
31 
32 *****  Author: Mark Jenkinson, FMRIB Centre, University of Oxford       *****
33 *****  Date:   September 2004                                           *****
34 
35 *****  Neither the FMRIB Centre, the University of Oxford, nor any of   *****
36 *****  its employees imply any warranty of usefulness of this software  *****
37 *****  for any purpose, and do not assume any liability for damages,    *****
38 *****  incidental or otherwise, caused by any use of this document.     *****
39 
40 */
41 
42 
43 /* Note extra argument (use_compression) where
44    use_compression==0 is no compression
45    use_compression!=0 uses zlib (gzip) compression
46 */
47 
znzopen(const char * path,const char * mode,int use_compression)48 znzFile znzopen(const char *path, const char *mode, int use_compression)
49 {
50   znzFile file;
51   file = (znzFile) mycalloc(1L,sizeof(struct znzptr));
52   if( file == NULL ){
53      fprintf(stderr,"** ERROR: znzopen failed to alloc znzptr\n");
54      return NULL;
55   }
56 
57   file->nzfptr = NULL;
58 
59 #ifdef HAVE_ZLIB
60   file->zfptr = NULL;
61 
62   if (use_compression) {
63     file->withz = 1;
64     if((file->zfptr = gzopen(path,mode)) == NULL) {
65         free(file);
66         file = NULL;
67     }
68   } else {
69 #endif
70 
71     file->withz = 0;
72     if((file->nzfptr = fopen(path,mode)) == NULL) {
73       free(file);
74       file = NULL;
75     }
76 
77 #ifdef HAVE_ZLIB
78   }
79 #endif
80 
81   return file;
82 }
83 
84 // added Peter Beerli 2010
85 // using zlib 1.2.5 (it is not present in 1.2.3 on macs)
znzbuffer(znzFile file,unsigned long size)86 int znzbuffer(znzFile file, unsigned long size)
87 {
88 #ifdef  HAVE_GZBUFFER
89   return gzbuffer(file,size);
90 #else
91   return 0;
92 #endif
93 }
94 
95 
znzdopen(int fd,const char * mode,int use_compression)96 znzFile znzdopen(int fd, const char *mode, int use_compression)
97 {
98   znzFile file;
99   file = (znzFile) mycalloc(1,sizeof(struct znzptr));
100   if( file == NULL ){
101      fprintf(stderr,"** ERROR: znzdopen failed to alloc znzptr\n");
102      return NULL;
103   }
104 #ifdef HAVE_ZLIB
105   if (use_compression) {
106     file->withz = 1;
107     file->zfptr = gzdopen(fd,mode);
108     file->nzfptr = NULL;
109   } else {
110 #endif
111     file->withz = 0;
112 #ifdef HAVE_FDOPEN
113     file->nzfptr = fdopen(fd,mode);
114 #endif
115 #ifdef HAVE_ZLIB
116     file->zfptr = NULL;
117   };
118 #endif
119   return file;
120 }
121 
122 
Xznzclose(znzFile * file)123 int Xznzclose(znzFile * file)
124 {
125   int retval = 0;
126   if (*file!=NULL) {
127 #ifdef HAVE_ZLIB
128     if ((*file)->zfptr!=NULL)  { retval = gzclose((*file)->zfptr); }
129 #endif
130     if ((*file)->nzfptr!=NULL) { retval = fclose((*file)->nzfptr); }
131 
132     free(*file);
133     *file = NULL;
134   }
135   return retval;
136 }
137 
138 
139 /* we already assume ints are 4 bytes */
140 #undef ZNZ_MAX_BLOCK_SIZE
141 #define ZNZ_MAX_BLOCK_SIZE (1<<30)
142 
znzread(void * buf,size_t size,size_t nmemb,znzFile file)143 size_t znzread(void* buf, size_t size, size_t nmemb, znzFile file)
144 {
145   size_t     remain = size*nmemb;
146   char     * cbuf = (char *)buf;
147   unsigned   n2read;
148   int        nread;
149 
150   if (file==NULL) { return 0; }
151 #ifdef HAVE_ZLIB
152   if (file->zfptr!=NULL) {
153     /* gzread/write take unsigned int length, so maybe read in int pieces
154        (noted by M Hanke, example given by M Adler)   6 July 2010 [rickr] */
155     while( remain > 0 ) {
156        n2read = (remain < ZNZ_MAX_BLOCK_SIZE) ? remain : ZNZ_MAX_BLOCK_SIZE;
157        nread = gzread(file->zfptr, (void *)cbuf, n2read);
158        if( nread < 0 ) return nread; /* returns -1 on error */
159 
160        remain -= nread;
161        cbuf += nread;
162 
163        /* require reading n2read bytes, so we don't get stuck */
164        if( nread < (int)n2read ) break;  /* return will be short */
165     }
166 
167     /* warn of a short read that will seem complete */
168     if( remain > 0 && remain < size )
169        fprintf(stderr,"** znzread: read short by %u bytes\n",(unsigned)remain);
170 
171     return nmemb - remain/size;   /* return number of members processed */
172   }
173 #endif
174   return fread(buf,size,nmemb,file->nzfptr);
175 }
176 
znzwrite(const void * buf,size_t size,size_t nmemb,znzFile file)177 size_t znzwrite(const void* buf, size_t size, size_t nmemb, znzFile file)
178 {
179   size_t     remain = size*nmemb;
180   char     * cbuf = (char *)buf;
181   unsigned   n2write;
182   int        nwritten;
183 
184   if (file==NULL) { return 0; }
185 #ifdef HAVE_ZLIB
186   if (file->zfptr!=NULL) {
187     while( remain > 0 ) {
188        n2write = (remain < ZNZ_MAX_BLOCK_SIZE) ? remain : ZNZ_MAX_BLOCK_SIZE;
189        nwritten = gzwrite(file->zfptr, (void *)cbuf, n2write);
190 
191        /* gzread returns 0 on error, but in case that ever changes... */
192        if( nwritten < 0 ) return nwritten;
193 
194        remain -= nwritten;
195        cbuf += nwritten;
196 
197        /* require writing n2write bytes, so we don't get stuck */
198        if( nwritten < (int)n2write ) break;
199     }
200 
201     /* warn of a short write that will seem complete */
202     if( remain > 0 && remain < size )
203       fprintf(stderr,"** znzwrite: write short by %u bytes\n",(unsigned)remain);
204 
205     return nmemb - remain/size;   /* return number of members processed */
206   }
207 #endif
208   return fwrite(buf,size,nmemb,file->nzfptr);
209 }
210 
znzseek(znzFile file,long offset,int whence)211 long znzseek(znzFile file, long offset, int whence)
212 {
213   if (file==NULL) { return 0; }
214 #ifdef HAVE_ZLIB
215   if (file->zfptr!=NULL) return (long) gzseek(file->zfptr,offset,whence);
216 #endif
217   return fseek(file->nzfptr,offset,whence);
218 }
219 
znzrewind(znzFile stream)220 int znzrewind(znzFile stream)
221 {
222   if (stream==NULL) { return 0; }
223 #ifdef HAVE_ZLIB
224   /* On some systems, gzrewind() fails for uncompressed files.
225      Use gzseek(), instead.               10, May 2005 [rickr]
226 
227      if (stream->zfptr!=NULL) return gzrewind(stream->zfptr);
228   */
229 
230   if (stream->zfptr!=NULL) return (int)gzseek(stream->zfptr, 0L, SEEK_SET);
231 #endif
232   rewind(stream->nzfptr);
233   return 0;
234 }
235 
znztell(znzFile file)236 long znztell(znzFile file)
237 {
238   if (file==NULL) { return 0; }
239 #ifdef HAVE_ZLIB
240   if (file->zfptr!=NULL) return (long) gztell(file->zfptr);
241 #endif
242   return ftell(file->nzfptr);
243 }
244 
znzputs(const char * str,znzFile file)245 int znzputs(const char * str, znzFile file)
246 {
247   if (file==NULL) { return 0; }
248 #ifdef HAVE_ZLIB
249   if (file->zfptr!=NULL) return gzputs(file->zfptr,str);
250 #endif
251   return fputs(str,file->nzfptr);
252 }
253 
254 
znzgets(char * str,int size,znzFile file)255 char * znzgets(char* str, int size, znzFile file)
256 {
257   if (file==NULL) { return NULL; }
258 #ifdef HAVE_ZLIB
259   if (file->zfptr!=NULL) return gzgets(file->zfptr,str,size);
260 #endif
261   return fgets(str,size,file->nzfptr);
262 }
263 
264 
znzflush(znzFile file)265 int znzflush(znzFile file)
266 {
267   if (file==NULL) { return 0; }
268 #ifdef HAVE_ZLIB
269   if (file->zfptr!=NULL) return gzflush(file->zfptr,Z_SYNC_FLUSH);
270 #endif
271   return fflush(file->nzfptr);
272 }
273 
274 
znzeof(znzFile file)275 int znzeof(znzFile file)
276 {
277   if (file==NULL) { return 0; }
278 #ifdef HAVE_ZLIB
279   if (file->zfptr!=NULL) return gzeof(file->zfptr);
280 #endif
281   return feof(file->nzfptr);
282 }
283 
284 
znzputc(int c,znzFile file)285 int znzputc(int c, znzFile file)
286 {
287   if (file==NULL) { return 0; }
288 #ifdef HAVE_ZLIB
289   if (file->zfptr!=NULL) return gzputc(file->zfptr,c);
290 #endif
291   return fputc(c,file->nzfptr);
292 }
293 
znzungetc(int c,znzFile file)294 int znzungetc(int c, znzFile file)
295 {
296   if (file==NULL) { return 0; }
297 #ifdef HAVE_ZLIB
298   if (file->zfptr!=NULL) return gzungetc(c, file->zfptr);
299 #endif
300   return ungetc(c,file->nzfptr);
301 }
302 
znzgetc(znzFile file)303 int znzgetc(znzFile file)
304 {
305   if (file==NULL) { return 0; }
306 #ifdef HAVE_ZLIB
307   if (file->zfptr!=NULL) return gzgetc(file->zfptr);
308 #endif
309   return fgetc(file->nzfptr);
310 }
311 
312 #if !defined (WIN32)
znzprintf(znzFile stream,const char * format,...)313 int znzprintf(znzFile stream, const char *format, ...)
314 {
315   int retval=0;
316   char *tmpstr;
317   va_list va;
318   if (stream==NULL) { return 0; }
319   va_start(va, format);
320 #ifdef HAVE_ZLIB
321   if (stream->zfptr!=NULL) {
322     int size;  /* local to HAVE_ZLIB block */
323     size = strlen(format) + 1000000;  /* overkill I hope */
324     tmpstr = (char *) mycalloc(1L, size);
325     if( tmpstr == NULL ){
326        fprintf(stderr,"** ERROR: znzprintf failed to alloc %d bytes\n", size);
327        return retval;
328     }
329     vsprintf(tmpstr,format,va);
330     retval=gzprintf(stream->zfptr,"%s",tmpstr);
331     free(tmpstr);
332   } else
333 #endif
334   {
335    retval=vfprintf(stream->nzfptr,format,va);
336   }
337   va_end(va);
338   return retval;
339 }
340 
341 #endif
342 
343 #endif
344