xref: /src/sys/contrib/zlib/gzwrite.c (revision 7aa1dba6b00ccfb7d66627badc8a7aaa06b02946)
1 /* gzwrite.c -- zlib functions for writing gzip files
2  * Copyright (C) 2004-2026 Mark Adler
3  * For conditions of distribution and use, see copyright notice in zlib.h
4  */
5 
6 #include "gzguts.h"
7 #include <unistd.h>
8 
9 /* Initialize state for writing a gzip file.  Mark initialization by setting
10    state->size to non-zero.  Return -1 on a memory allocation failure, or 0 on
11    success. */
gz_init(gz_statep state)12 local int gz_init(gz_statep state) {
13     int ret;
14     z_streamp strm = &(state->strm);
15 
16     /* allocate input buffer (double size for gzprintf) */
17     state->in = (unsigned char *)malloc(state->want << 1);
18     if (state->in == NULL) {
19         gz_error(state, Z_MEM_ERROR, "out of memory");
20         return -1;
21     }
22 
23     /* only need output buffer and deflate state if compressing */
24     if (!state->direct) {
25         /* allocate output buffer */
26         state->out = (unsigned char *)malloc(state->want);
27         if (state->out == NULL) {
28             free(state->in);
29             gz_error(state, Z_MEM_ERROR, "out of memory");
30             return -1;
31         }
32 
33         /* allocate deflate memory, set up for gzip compression */
34         strm->zalloc = Z_NULL;
35         strm->zfree = Z_NULL;
36         strm->opaque = Z_NULL;
37         ret = deflateInit2(strm, state->level, Z_DEFLATED,
38                            MAX_WBITS + 16, DEF_MEM_LEVEL, state->strategy);
39         if (ret != Z_OK) {
40             free(state->out);
41             free(state->in);
42             gz_error(state, Z_MEM_ERROR, "out of memory");
43             return -1;
44         }
45         strm->next_in = NULL;
46     }
47 
48     /* mark state as initialized */
49     state->size = state->want;
50 
51     /* initialize write buffer if compressing */
52     if (!state->direct) {
53         strm->avail_out = state->size;
54         strm->next_out = state->out;
55         state->x.next = strm->next_out;
56     }
57     return 0;
58 }
59 
60 /* Compress whatever is at avail_in and next_in and write to the output file.
61    Return -1 if there is an error writing to the output file or if gz_init()
62    fails to allocate memory, otherwise 0.  flush is assumed to be a valid
63    deflate() flush value.  If flush is Z_FINISH, then the deflate() state is
64    reset to start a new gzip stream.  If gz->direct is true, then simply write
65    to the output file without compressing, and ignore flush. */
gz_comp(gz_statep state,int flush)66 local int gz_comp(gz_statep state, int flush) {
67     int ret, writ;
68     unsigned have, put, max = ((unsigned)-1 >> 2) + 1;
69     z_streamp strm = &(state->strm);
70 
71     /* allocate memory if this is the first time through */
72     if (state->size == 0 && gz_init(state) == -1)
73         return -1;
74 
75     /* write directly if requested */
76     if (state->direct) {
77         while (strm->avail_in) {
78             errno = 0;
79             state->again = 0;
80             put = strm->avail_in > max ? max : strm->avail_in;
81             writ = (int)write(state->fd, strm->next_in, put);
82             if (writ < 0) {
83                 if (errno == EAGAIN || errno == EWOULDBLOCK)
84                     state->again = 1;
85                 gz_error(state, Z_ERRNO, zstrerror());
86                 return -1;
87             }
88             strm->avail_in -= (unsigned)writ;
89             strm->next_in += writ;
90         }
91         return 0;
92     }
93 
94     /* check for a pending reset */
95     if (state->reset) {
96         /* don't start a new gzip member unless there is data to write and
97            we're not flushing */
98         if (strm->avail_in == 0 && flush == Z_NO_FLUSH)
99             return 0;
100         deflateReset(strm);
101         state->reset = 0;
102     }
103 
104     /* run deflate() on provided input until it produces no more output */
105     ret = Z_OK;
106     do {
107         /* write out current buffer contents if full, or if flushing, but if
108            doing Z_FINISH then don't write until we get to Z_STREAM_END */
109         if (strm->avail_out == 0 || (flush != Z_NO_FLUSH &&
110             (flush != Z_FINISH || ret == Z_STREAM_END))) {
111             while (strm->next_out > state->x.next) {
112                 errno = 0;
113                 state->again = 0;
114                 put = strm->next_out - state->x.next > (int)max ? max :
115                       (unsigned)(strm->next_out - state->x.next);
116                 writ = (int)write(state->fd, state->x.next, put);
117                 if (writ < 0) {
118                     if (errno == EAGAIN || errno == EWOULDBLOCK)
119                         state->again = 1;
120                     gz_error(state, Z_ERRNO, zstrerror());
121                     return -1;
122                 }
123                 state->x.next += writ;
124             }
125             if (strm->avail_out == 0) {
126                 strm->avail_out = state->size;
127                 strm->next_out = state->out;
128                 state->x.next = state->out;
129             }
130         }
131 
132         /* compress */
133         have = strm->avail_out;
134         ret = deflate(strm, flush);
135         if (ret == Z_STREAM_ERROR) {
136             gz_error(state, Z_STREAM_ERROR,
137                       "internal error: deflate stream corrupt");
138             return -1;
139         }
140         have -= strm->avail_out;
141     } while (have);
142 
143     /* if that completed a deflate stream, allow another to start */
144     if (flush == Z_FINISH)
145         state->reset = 1;
146 
147     /* all done, no errors */
148     return 0;
149 }
150 
151 /* Compress state->skip (> 0) zeros to output.  Return -1 on a write error or
152    memory allocation failure by gz_comp(), or 0 on success. state->skip is
153    updated with the number of successfully written zeros, in case there is a
154    stall on a non-blocking write destination. */
gz_zero(gz_statep state)155 local int gz_zero(gz_statep state) {
156     int first, ret;
157     unsigned n;
158     z_streamp strm = &(state->strm);
159 
160     /* consume whatever's left in the input buffer */
161     if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1)
162         return -1;
163 
164     /* compress state->skip zeros */
165     first = 1;
166     do {
167         n = GT_OFF(state->size) || (z_off64_t)state->size > state->skip ?
168             (unsigned)state->skip : state->size;
169         if (first) {
170             memset(state->in, 0, n);
171             first = 0;
172         }
173         strm->avail_in = n;
174         strm->next_in = state->in;
175         ret = gz_comp(state, Z_NO_FLUSH);
176         n -= strm->avail_in;
177         state->x.pos += n;
178         state->skip -= n;
179         if (ret == -1)
180             return -1;
181     } while (state->skip);
182     return 0;
183 }
184 
185 /* Write len bytes from buf to file.  Return the number of bytes written.  If
186    the returned value is less than len, then there was an error. If the error
187    was a non-blocking stall, then the number of bytes consumed is returned.
188    For any other error, 0 is returned. */
gz_write(gz_statep state,voidpc buf,z_size_t len)189 local z_size_t gz_write(gz_statep state, voidpc buf, z_size_t len) {
190     z_size_t put = len;
191     int ret;
192 
193     /* if len is zero, avoid unnecessary operations */
194     if (len == 0)
195         return 0;
196 
197     /* allocate memory if this is the first time through */
198     if (state->size == 0 && gz_init(state) == -1)
199         return 0;
200 
201     /* check for seek request */
202     if (state->skip && gz_zero(state) == -1)
203         return 0;
204 
205     /* for small len, copy to input buffer, otherwise compress directly */
206     if (len < state->size) {
207         /* copy to input buffer, compress when full */
208         for (;;) {
209             unsigned have, copy;
210 
211             if (state->strm.avail_in == 0)
212                 state->strm.next_in = state->in;
213             have = (unsigned)((state->strm.next_in + state->strm.avail_in) -
214                               state->in);
215             copy = state->size - have;
216             if (copy > len)
217                 copy = (unsigned)len;
218             memcpy(state->in + have, buf, copy);
219             state->strm.avail_in += copy;
220             state->x.pos += copy;
221             buf = (const char *)buf + copy;
222             len -= copy;
223             if (len == 0)
224                 break;
225             if (gz_comp(state, Z_NO_FLUSH) == -1)
226                 return state->again ? put - len : 0;
227         }
228     }
229     else {
230         /* consume whatever's left in the input buffer */
231         if (state->strm.avail_in && gz_comp(state, Z_NO_FLUSH) == -1)
232             return 0;
233 
234         /* directly compress user buffer to file */
235         state->strm.next_in = (z_const Bytef *)buf;
236         do {
237             unsigned n = (unsigned)-1;
238 
239             if (n > len)
240                 n = (unsigned)len;
241             state->strm.avail_in = n;
242             ret = gz_comp(state, Z_NO_FLUSH);
243             n -= state->strm.avail_in;
244             state->x.pos += n;
245             len -= n;
246             if (ret == -1)
247                 return state->again ? put - len : 0;
248         } while (len);
249     }
250 
251     /* input was all buffered or compressed */
252     return put;
253 }
254 
255 /* -- see zlib.h -- */
gzwrite(gzFile file,voidpc buf,unsigned len)256 int ZEXPORT gzwrite(gzFile file, voidpc buf, unsigned len) {
257     gz_statep state;
258 
259     /* get internal structure */
260     if (file == NULL)
261         return 0;
262     state = (gz_statep)file;
263 
264     /* check that we're writing and that there's no (serious) error */
265     if (state->mode != GZ_WRITE || (state->err != Z_OK && !state->again))
266         return 0;
267     gz_error(state, Z_OK, NULL);
268 
269     /* since an int is returned, make sure len fits in one, otherwise return
270        with an error (this avoids a flaw in the interface) */
271     if ((int)len < 0) {
272         gz_error(state, Z_DATA_ERROR, "requested length does not fit in int");
273         return 0;
274     }
275 
276     /* write len bytes from buf (the return value will fit in an int) */
277     return (int)gz_write(state, buf, len);
278 }
279 
280 /* -- see zlib.h -- */
gzfwrite(voidpc buf,z_size_t size,z_size_t nitems,gzFile file)281 z_size_t ZEXPORT gzfwrite(voidpc buf, z_size_t size, z_size_t nitems,
282                           gzFile file) {
283     z_size_t len;
284     gz_statep state;
285 
286     /* get internal structure */
287     if (file == NULL)
288         return 0;
289     state = (gz_statep)file;
290 
291     /* check that we're writing and that there's no (serious) error */
292     if (state->mode != GZ_WRITE || (state->err != Z_OK && !state->again))
293         return 0;
294     gz_error(state, Z_OK, NULL);
295 
296     /* compute bytes to read -- error on overflow */
297     len = nitems * size;
298     if (size && len / size != nitems) {
299         gz_error(state, Z_STREAM_ERROR, "request does not fit in a size_t");
300         return 0;
301     }
302 
303     /* write len bytes to buf, return the number of full items written */
304     return len ? gz_write(state, buf, len) / size : 0;
305 }
306 
307 /* -- see zlib.h -- */
gzputc(gzFile file,int c)308 int ZEXPORT gzputc(gzFile file, int c) {
309     unsigned have;
310     unsigned char buf[1];
311     gz_statep state;
312     z_streamp strm;
313 
314     /* get internal structure */
315     if (file == NULL)
316         return -1;
317     state = (gz_statep)file;
318     strm = &(state->strm);
319 
320     /* check that we're writing and that there's no (serious) error */
321     if (state->mode != GZ_WRITE || (state->err != Z_OK && !state->again))
322         return -1;
323     gz_error(state, Z_OK, NULL);
324 
325     /* check for seek request */
326     if (state->skip && gz_zero(state) == -1)
327         return -1;
328 
329     /* try writing to input buffer for speed (state->size == 0 if buffer not
330        initialized) */
331     if (state->size) {
332         if (strm->avail_in == 0)
333             strm->next_in = state->in;
334         have = (unsigned)((strm->next_in + strm->avail_in) - state->in);
335         if (have < state->size) {
336             state->in[have] = (unsigned char)c;
337             strm->avail_in++;
338             state->x.pos++;
339             return c & 0xff;
340         }
341     }
342 
343     /* no room in buffer or not initialized, use gz_write() */
344     buf[0] = (unsigned char)c;
345     if (gz_write(state, buf, 1) != 1)
346         return -1;
347     return c & 0xff;
348 }
349 
350 /* -- see zlib.h -- */
gzputs(gzFile file,const char * s)351 int ZEXPORT gzputs(gzFile file, const char *s) {
352     z_size_t len, put;
353     gz_statep state;
354 
355     /* get internal structure */
356     if (file == NULL)
357         return -1;
358     state = (gz_statep)file;
359 
360     /* check that we're writing and that there's no (serious) error */
361     if (state->mode != GZ_WRITE || (state->err != Z_OK && !state->again))
362         return -1;
363     gz_error(state, Z_OK, NULL);
364 
365     /* write string */
366     len = strlen(s);
367     if ((int)len < 0 || (unsigned)len != len) {
368         gz_error(state, Z_STREAM_ERROR, "string length does not fit in int");
369         return -1;
370     }
371     put = gz_write(state, s, len);
372     return len && put == 0 ? -1 : (int)put;
373 }
374 
375 #if (((!defined(STDC) && !defined(Z_HAVE_STDARG_H)) || !defined(NO_vsnprintf)) && \
376      (defined(STDC) || defined(Z_HAVE_STDARG_H) || !defined(NO_snprintf))) || \
377     defined(ZLIB_INSECURE)
378 /* If the second half of the input buffer is occupied, write out the contents.
379    If there is any input remaining due to a non-blocking stall on write, move
380    it to the start of the buffer. Return true if this did not open up the
381    second half of the buffer.  state->err should be checked after this to
382    handle a gz_comp() error. */
gz_vacate(gz_statep state)383 local int gz_vacate(gz_statep state) {
384     z_streamp strm;
385 
386     strm = &(state->strm);
387     if (strm->next_in + strm->avail_in <= state->in + state->size)
388         return 0;
389     (void)gz_comp(state, Z_NO_FLUSH);
390     if (strm->avail_in == 0) {
391         strm->next_in = state->in;
392         return 0;
393     }
394     memmove(state->in, strm->next_in, strm->avail_in);
395     strm->next_in = state->in;
396     return strm->avail_in > state->size;
397 }
398 #endif
399 
400 #if defined(STDC) || defined(Z_HAVE_STDARG_H)
401 #include <stdarg.h>
402 
403 /* -- see zlib.h -- */
gzvprintf(gzFile file,const char * format,va_list va)404 int ZEXPORTVA gzvprintf(gzFile file, const char *format, va_list va) {
405 #if defined(NO_vsnprintf) && !defined(ZLIB_INSECURE)
406 #warning "vsnprintf() not available -- gzprintf() stub returns Z_STREAM_ERROR"
407 #warning "you can recompile with ZLIB_INSECURE defined to use vsprintf()"
408     /* prevent use of insecure vsprintf(), unless purposefully requested */
409     (void)file, (void)format, (void)va;
410     return Z_STREAM_ERROR;
411 #else
412     int len, ret;
413     char *next;
414     gz_statep state;
415     z_streamp strm;
416 
417     /* get internal structure */
418     if (file == NULL)
419         return Z_STREAM_ERROR;
420     state = (gz_statep)file;
421     strm = &(state->strm);
422 
423     /* check that we're writing and that there's no (serious) error */
424     if (state->mode != GZ_WRITE || (state->err != Z_OK && !state->again))
425         return Z_STREAM_ERROR;
426     gz_error(state, Z_OK, NULL);
427 
428     /* make sure we have some buffer space */
429     if (state->size == 0 && gz_init(state) == -1)
430         return state->err;
431 
432     /* check for seek request */
433     if (state->skip && gz_zero(state) == -1)
434         return state->err;
435 
436     /* do the printf() into the input buffer, put length in len -- the input
437        buffer is double-sized just for this function, so there should be
438        state->size bytes available after the current contents */
439     ret = gz_vacate(state);
440     if (state->err) {
441         if (ret && state->again) {
442             /* There was a non-blocking stall on write, resulting in the part
443                of the second half of the output buffer being occupied.  Return
444                a Z_BUF_ERROR to let the application know that this gzprintf()
445                needs to be retried. */
446             gz_error(state, Z_BUF_ERROR, "stalled write on gzprintf");
447         }
448         if (!state->again)
449             return state->err;
450     }
451     if (strm->avail_in == 0)
452         strm->next_in = state->in;
453     next = (char *)(state->in + (strm->next_in - state->in) + strm->avail_in);
454     next[state->size - 1] = 0;
455 #ifdef NO_vsnprintf
456 #  ifdef HAS_vsprintf_void
457     (void)vsprintf(next, format, va);
458     for (len = 0; len < state->size; len++)
459         if (next[len] == 0) break;
460 #  else
461     len = vsprintf(next, format, va);
462 #  endif
463 #else
464 #  ifdef HAS_vsnprintf_void
465     (void)vsnprintf(next, state->size, format, va);
466     len = strlen(next);
467 #  else
468     len = vsnprintf(next, state->size, format, va);
469 #  endif
470 #endif
471 
472     /* check that printf() results fit in buffer */
473     if (len == 0 || (unsigned)len >= state->size || next[state->size - 1] != 0)
474         return 0;
475 
476     /* update buffer and position */
477     strm->avail_in += (unsigned)len;
478     state->x.pos += len;
479 
480     /* write out buffer if more than half is occupied */
481     ret = gz_vacate(state);
482     if (state->err && !state->again)
483         return state->err;
484     return len;
485 #endif
486 }
487 
gzprintf(gzFile file,const char * format,...)488 int ZEXPORTVA gzprintf(gzFile file, const char *format, ...) {
489     va_list va;
490     int ret;
491 
492     va_start(va, format);
493     ret = gzvprintf(file, format, va);
494     va_end(va);
495     return ret;
496 }
497 
498 #else /* !STDC && !Z_HAVE_STDARG_H */
499 
500 /* -- see zlib.h -- */
gzprintf(gzFile file,const char * format,int a1,int a2,int a3,int a4,int a5,int a6,int a7,int a8,int a9,int a10,int a11,int a12,int a13,int a14,int a15,int a16,int a17,int a18,int a19,int a20)501 int ZEXPORTVA gzprintf(gzFile file, const char *format, int a1, int a2, int a3,
502                        int a4, int a5, int a6, int a7, int a8, int a9, int a10,
503                        int a11, int a12, int a13, int a14, int a15, int a16,
504                        int a17, int a18, int a19, int a20) {
505 #if defined(NO_snprintf) && !defined(ZLIB_INSECURE)
506 #warning "snprintf() not available -- gzprintf() stub returns Z_STREAM_ERROR"
507 #warning "you can recompile with ZLIB_INSECURE defined to use sprintf()"
508     /* prevent use of insecure sprintf(), unless purposefully requested */
509     (void)file, (void)format, (void)a1, (void)a2, (void)a3, (void)a4, (void)a5,
510     (void)a6, (void)a7, (void)a8, (void)a9, (void)a10, (void)a11, (void)a12,
511     (void)a13, (void)a14, (void)a15, (void)a16, (void)a17, (void)a18,
512     (void)a19, (void)a20;
513     return Z_STREAM_ERROR;
514 #else
515     int ret;
516     unsigned len, left;
517     char *next;
518     gz_statep state;
519     z_streamp strm;
520 
521     /* get internal structure */
522     if (file == NULL)
523         return Z_STREAM_ERROR;
524     state = (gz_statep)file;
525     strm = &(state->strm);
526 
527     /* check that can really pass pointer in ints */
528     if (sizeof(int) != sizeof(void *))
529         return Z_STREAM_ERROR;
530 
531     /* check that we're writing and that there's no (serious) error */
532     if (state->mode != GZ_WRITE || (state->err != Z_OK && !state->again))
533         return Z_STREAM_ERROR;
534     gz_error(state, Z_OK, NULL);
535 
536     /* make sure we have some buffer space */
537     if (state->size == 0 && gz_init(state) == -1)
538         return state->err;
539 
540     /* check for seek request */
541     if (state->skip && gz_zero(state) == -1)
542         return state->err;
543 
544     /* do the printf() into the input buffer, put length in len -- the input
545        buffer is double-sized just for this function, so there is guaranteed to
546        be state->size bytes available after the current contents */
547     ret = gz_vacate(state);
548     if (state->err) {
549         if (ret && state->again) {
550             /* There was a non-blocking stall on write, resulting in the part
551                of the second half of the output buffer being occupied.  Return
552                a Z_BUF_ERROR to let the application know that this gzprintf()
553                needs to be retried. */
554             gz_error(state, Z_BUF_ERROR, "stalled write on gzprintf");
555         }
556         if (!state->again)
557             return state->err;
558     }
559     if (strm->avail_in == 0)
560         strm->next_in = state->in;
561     next = (char *)(strm->next_in + strm->avail_in);
562     next[state->size - 1] = 0;
563 #ifdef NO_snprintf
564 #  ifdef HAS_sprintf_void
565     sprintf(next, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12,
566             a13, a14, a15, a16, a17, a18, a19, a20);
567     for (len = 0; len < size; len++)
568         if (next[len] == 0)
569             break;
570 #  else
571     len = sprintf(next, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11,
572                   a12, a13, a14, a15, a16, a17, a18, a19, a20);
573 #  endif
574 #else
575 #  ifdef HAS_snprintf_void
576     snprintf(next, state->size, format, a1, a2, a3, a4, a5, a6, a7, a8, a9,
577              a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20);
578     len = strlen(next);
579 #  else
580     len = snprintf(next, state->size, format, a1, a2, a3, a4, a5, a6, a7, a8,
581                    a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20);
582 #  endif
583 #endif
584 
585     /* check that printf() results fit in buffer */
586     if (len == 0 || len >= state->size || next[state->size - 1] != 0)
587         return 0;
588 
589     /* update buffer and position, compress first half if past that */
590     strm->avail_in += len;
591     state->x.pos += len;
592 
593     /* write out buffer if more than half is occupied */
594     ret = gz_vacate(state);
595     if (state->err && !state->again)
596         return state->err;
597     return (int)len;
598 #endif
599 }
600 
601 #endif
602 
603 /* -- see zlib.h -- */
gzflush(gzFile file,int flush)604 int ZEXPORT gzflush(gzFile file, int flush) {
605     gz_statep state;
606 
607     /* get internal structure */
608     if (file == NULL)
609         return Z_STREAM_ERROR;
610     state = (gz_statep)file;
611 
612     /* check that we're writing and that there's no (serious) error */
613     if (state->mode != GZ_WRITE || (state->err != Z_OK && !state->again))
614         return Z_STREAM_ERROR;
615     gz_error(state, Z_OK, NULL);
616 
617     /* check flush parameter */
618     if (flush < 0 || flush > Z_FINISH)
619         return Z_STREAM_ERROR;
620 
621     /* check for seek request */
622     if (state->skip && gz_zero(state) == -1)
623         return state->err;
624 
625     /* compress remaining data with requested flush */
626     (void)gz_comp(state, flush);
627     return state->err;
628 }
629 
630 /* -- see zlib.h -- */
gzsetparams(gzFile file,int level,int strategy)631 int ZEXPORT gzsetparams(gzFile file, int level, int strategy) {
632     gz_statep state;
633     z_streamp strm;
634 
635     /* get internal structure */
636     if (file == NULL)
637         return Z_STREAM_ERROR;
638     state = (gz_statep)file;
639     strm = &(state->strm);
640 
641     /* check that we're compressing and that there's no (serious) error */
642     if (state->mode != GZ_WRITE || (state->err != Z_OK && !state->again) ||
643             state->direct)
644         return Z_STREAM_ERROR;
645     gz_error(state, Z_OK, NULL);
646 
647     /* if no change is requested, then do nothing */
648     if (level == state->level && strategy == state->strategy)
649         return Z_OK;
650 
651     /* check for seek request */
652     if (state->skip && gz_zero(state) == -1)
653         return state->err;
654 
655     /* change compression parameters for subsequent input */
656     if (state->size) {
657         /* flush previous input with previous parameters before changing */
658         if (strm->avail_in && gz_comp(state, Z_BLOCK) == -1)
659             return state->err;
660         deflateParams(strm, level, strategy);
661     }
662     state->level = level;
663     state->strategy = strategy;
664     return Z_OK;
665 }
666 
667 /* -- see zlib.h -- */
gzclose_w(gzFile file)668 int ZEXPORT gzclose_w(gzFile file) {
669     int ret = Z_OK;
670     gz_statep state;
671 
672     /* get internal structure */
673     if (file == NULL)
674         return Z_STREAM_ERROR;
675     state = (gz_statep)file;
676 
677     /* check that we're writing */
678     if (state->mode != GZ_WRITE)
679         return Z_STREAM_ERROR;
680 
681     /* check for seek request */
682     if (state->skip && gz_zero(state) == -1)
683         ret = state->err;
684 
685     /* flush, free memory, and close file */
686     if (gz_comp(state, Z_FINISH) == -1)
687         ret = state->err;
688     if (state->size) {
689         if (!state->direct) {
690             (void)deflateEnd(&(state->strm));
691             free(state->out);
692         }
693         free(state->in);
694     }
695     gz_error(state, Z_OK, NULL);
696     free(state->path);
697     if (close(state->fd) == -1)
698         ret = Z_ERRNO;
699     free(state);
700     return ret;
701 }
702