xref: /freebsd/sys/contrib/zlib/gzwrite.c (revision c4fde87a3236b500246d4fce76dbca2a3d64f73a)
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. */
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. */
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. */
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. */
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                 state->strm.avail_in = 0;
248                 state->strm.next_in = state->in;
249                 return state->again ? put - len : 0;
250             }
251         } while (len);
252     }
253 
254     /* input was all buffered or compressed */
255     return put;
256 }
257 
258 /* -- see zlib.h -- */
259 int ZEXPORT gzwrite(gzFile file, voidpc buf, unsigned len) {
260     gz_statep state;
261 
262     /* get internal structure */
263     if (file == NULL)
264         return 0;
265     state = (gz_statep)file;
266 
267     /* check that we're writing and that there's no (serious) error */
268     if (state->mode != GZ_WRITE || (state->err != Z_OK && !state->again))
269         return 0;
270     gz_error(state, Z_OK, NULL);
271 
272     /* since an int is returned, make sure len fits in one, otherwise return
273        with an error (this avoids a flaw in the interface) */
274     if ((int)len < 0) {
275         gz_error(state, Z_DATA_ERROR, "requested length does not fit in int");
276         return 0;
277     }
278 
279     /* write len bytes from buf (the return value will fit in an int) */
280     return (int)gz_write(state, buf, len);
281 }
282 
283 /* -- see zlib.h -- */
284 z_size_t ZEXPORT gzfwrite(voidpc buf, z_size_t size, z_size_t nitems,
285                           gzFile file) {
286     z_size_t len;
287     gz_statep state;
288 
289     /* get internal structure */
290     if (file == NULL)
291         return 0;
292     state = (gz_statep)file;
293 
294     /* check that we're writing and that there's no (serious) error */
295     if (state->mode != GZ_WRITE || (state->err != Z_OK && !state->again))
296         return 0;
297     gz_error(state, Z_OK, NULL);
298 
299     /* compute bytes to read -- error on overflow */
300     len = nitems * size;
301     if (size && len / size != nitems) {
302         gz_error(state, Z_STREAM_ERROR, "request does not fit in a size_t");
303         return 0;
304     }
305 
306     /* write len bytes to buf, return the number of full items written */
307     return len ? gz_write(state, buf, len) / size : 0;
308 }
309 
310 /* -- see zlib.h -- */
311 int ZEXPORT gzputc(gzFile file, int c) {
312     unsigned have;
313     unsigned char buf[1];
314     gz_statep state;
315     z_streamp strm;
316 
317     /* get internal structure */
318     if (file == NULL)
319         return -1;
320     state = (gz_statep)file;
321     strm = &(state->strm);
322 
323     /* check that we're writing and that there's no (serious) error */
324     if (state->mode != GZ_WRITE || (state->err != Z_OK && !state->again))
325         return -1;
326     gz_error(state, Z_OK, NULL);
327 
328     /* check for seek request */
329     if (state->skip && gz_zero(state) == -1)
330         return -1;
331 
332     /* try writing to input buffer for speed (state->size == 0 if buffer not
333        initialized) */
334     if (state->size) {
335         if (strm->avail_in == 0)
336             strm->next_in = state->in;
337         have = (unsigned)((strm->next_in + strm->avail_in) - state->in);
338         if (have < state->size) {
339             state->in[have] = (unsigned char)c;
340             strm->avail_in++;
341             state->x.pos++;
342             return c & 0xff;
343         }
344     }
345 
346     /* no room in buffer or not initialized, use gz_write() */
347     buf[0] = (unsigned char)c;
348     if (gz_write(state, buf, 1) != 1)
349         return -1;
350     return c & 0xff;
351 }
352 
353 /* -- see zlib.h -- */
354 int ZEXPORT gzputs(gzFile file, const char *s) {
355     z_size_t len, put;
356     gz_statep state;
357 
358     /* get internal structure */
359     if (file == NULL)
360         return -1;
361     state = (gz_statep)file;
362 
363     /* check that we're writing and that there's no (serious) error */
364     if (state->mode != GZ_WRITE || (state->err != Z_OK && !state->again))
365         return -1;
366     gz_error(state, Z_OK, NULL);
367 
368     /* write string */
369     len = strlen(s);
370     if ((int)len < 0 || (unsigned)len != len) {
371         gz_error(state, Z_STREAM_ERROR, "string length does not fit in int");
372         return -1;
373     }
374     put = gz_write(state, s, len);
375     return len && put == 0 ? -1 : (int)put;
376 }
377 
378 #if (((!defined(STDC) && !defined(Z_HAVE_STDARG_H)) || !defined(NO_vsnprintf)) && \
379      (defined(STDC) || defined(Z_HAVE_STDARG_H) || !defined(NO_snprintf))) || \
380     defined(ZLIB_INSECURE)
381 /* If the second half of the input buffer is occupied, write out the contents.
382    If there is any input remaining due to a non-blocking stall on write, move
383    it to the start of the buffer. Return true if this did not open up the
384    second half of the buffer.  state->err should be checked after this to
385    handle a gz_comp() error. */
386 local int gz_vacate(gz_statep state) {
387     z_streamp strm;
388 
389     strm = &(state->strm);
390     if (strm->next_in + strm->avail_in <= state->in + state->size)
391         return 0;
392     (void)gz_comp(state, Z_NO_FLUSH);
393     if (strm->avail_in == 0) {
394         strm->next_in = state->in;
395         return 0;
396     }
397     memmove(state->in, strm->next_in, strm->avail_in);
398     strm->next_in = state->in;
399     return strm->avail_in > state->size;
400 }
401 #endif
402 
403 #if defined(STDC) || defined(Z_HAVE_STDARG_H)
404 #include <stdarg.h>
405 
406 /* -- see zlib.h -- */
407 int ZEXPORTVA gzvprintf(gzFile file, const char *format, va_list va) {
408 #if defined(NO_vsnprintf) && !defined(ZLIB_INSECURE)
409 #warning "vsnprintf() not available -- gzprintf() stub returns Z_STREAM_ERROR"
410 #warning "you can recompile with ZLIB_INSECURE defined to use vsprintf()"
411     /* prevent use of insecure vsprintf(), unless purposefully requested */
412     (void)file, (void)format, (void)va;
413     return Z_STREAM_ERROR;
414 #else
415     int len, ret;
416     char *next;
417     gz_statep state;
418     z_streamp strm;
419 
420     /* get internal structure */
421     if (file == NULL)
422         return Z_STREAM_ERROR;
423     state = (gz_statep)file;
424     strm = &(state->strm);
425 
426     /* check that we're writing and that there's no (serious) error */
427     if (state->mode != GZ_WRITE || (state->err != Z_OK && !state->again))
428         return Z_STREAM_ERROR;
429     gz_error(state, Z_OK, NULL);
430 
431     /* make sure we have some buffer space */
432     if (state->size == 0 && gz_init(state) == -1)
433         return state->err;
434 
435     /* check for seek request */
436     if (state->skip && gz_zero(state) == -1)
437         return state->err;
438 
439     /* do the printf() into the input buffer, put length in len -- the input
440        buffer is double-sized just for this function, so there should be
441        state->size bytes available after the current contents */
442     ret = gz_vacate(state);
443     if (state->err) {
444         if (ret && state->again) {
445             /* There was a non-blocking stall on write, resulting in the part
446                of the second half of the output buffer being occupied.  Return
447                a Z_BUF_ERROR to let the application know that this gzprintf()
448                needs to be retried. */
449             gz_error(state, Z_BUF_ERROR, "stalled write on gzprintf");
450         }
451         if (!state->again)
452             return state->err;
453     }
454     if (strm->avail_in == 0)
455         strm->next_in = state->in;
456     next = (char *)(state->in + (strm->next_in - state->in) + strm->avail_in);
457     next[state->size - 1] = 0;
458 #ifdef NO_vsnprintf
459 #  ifdef HAS_vsprintf_void
460     (void)vsprintf(next, format, va);
461     for (len = 0; len < state->size; len++)
462         if (next[len] == 0) break;
463 #  else
464     len = vsprintf(next, format, va);
465 #  endif
466 #else
467 #  ifdef HAS_vsnprintf_void
468     (void)vsnprintf(next, state->size, format, va);
469     len = strlen(next);
470 #  else
471     len = vsnprintf(next, state->size, format, va);
472 #  endif
473 #endif
474 
475     /* check that printf() results fit in buffer */
476     if (len == 0 || (unsigned)len >= state->size || next[state->size - 1] != 0)
477         return 0;
478 
479     /* update buffer and position */
480     strm->avail_in += (unsigned)len;
481     state->x.pos += len;
482 
483     /* write out buffer if more than half is occupied */
484     ret = gz_vacate(state);
485     if (state->err && !state->again)
486         return state->err;
487     return len;
488 #endif
489 }
490 
491 int ZEXPORTVA gzprintf(gzFile file, const char *format, ...) {
492     va_list va;
493     int ret;
494 
495     va_start(va, format);
496     ret = gzvprintf(file, format, va);
497     va_end(va);
498     return ret;
499 }
500 
501 #else /* !STDC && !Z_HAVE_STDARG_H */
502 
503 /* -- see zlib.h -- */
504 int ZEXPORTVA gzprintf(gzFile file, const char *format, int a1, int a2, int a3,
505                        int a4, int a5, int a6, int a7, int a8, int a9, int a10,
506                        int a11, int a12, int a13, int a14, int a15, int a16,
507                        int a17, int a18, int a19, int a20) {
508 #if defined(NO_snprintf) && !defined(ZLIB_INSECURE)
509 #warning "snprintf() not available -- gzprintf() stub returns Z_STREAM_ERROR"
510 #warning "you can recompile with ZLIB_INSECURE defined to use sprintf()"
511     /* prevent use of insecure sprintf(), unless purposefully requested */
512     (void)file, (void)format, (void)a1, (void)a2, (void)a3, (void)a4, (void)a5,
513     (void)a6, (void)a7, (void)a8, (void)a9, (void)a10, (void)a11, (void)a12,
514     (void)a13, (void)a14, (void)a15, (void)a16, (void)a17, (void)a18,
515     (void)a19, (void)a20;
516     return Z_STREAM_ERROR;
517 #else
518     int ret;
519     unsigned len, left;
520     char *next;
521     gz_statep state;
522     z_streamp strm;
523 
524     /* get internal structure */
525     if (file == NULL)
526         return Z_STREAM_ERROR;
527     state = (gz_statep)file;
528     strm = &(state->strm);
529 
530     /* check that can really pass pointer in ints */
531     if (sizeof(int) != sizeof(void *))
532         return Z_STREAM_ERROR;
533 
534     /* check that we're writing and that there's no (serious) error */
535     if (state->mode != GZ_WRITE || (state->err != Z_OK && !state->again))
536         return Z_STREAM_ERROR;
537     gz_error(state, Z_OK, NULL);
538 
539     /* make sure we have some buffer space */
540     if (state->size == 0 && gz_init(state) == -1)
541         return state->err;
542 
543     /* check for seek request */
544     if (state->skip && gz_zero(state) == -1)
545         return state->err;
546 
547     /* do the printf() into the input buffer, put length in len -- the input
548        buffer is double-sized just for this function, so there is guaranteed to
549        be state->size bytes available after the current contents */
550     ret = gz_vacate(state);
551     if (state->err) {
552         if (ret && state->again) {
553             /* There was a non-blocking stall on write, resulting in the part
554                of the second half of the output buffer being occupied.  Return
555                a Z_BUF_ERROR to let the application know that this gzprintf()
556                needs to be retried. */
557             gz_error(state, Z_BUF_ERROR, "stalled write on gzprintf");
558         }
559         if (!state->again)
560             return state->err;
561     }
562     if (strm->avail_in == 0)
563         strm->next_in = state->in;
564     next = (char *)(strm->next_in + strm->avail_in);
565     next[state->size - 1] = 0;
566 #ifdef NO_snprintf
567 #  ifdef HAS_sprintf_void
568     sprintf(next, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12,
569             a13, a14, a15, a16, a17, a18, a19, a20);
570     for (len = 0; len < size; len++)
571         if (next[len] == 0)
572             break;
573 #  else
574     len = sprintf(next, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11,
575                   a12, a13, a14, a15, a16, a17, a18, a19, a20);
576 #  endif
577 #else
578 #  ifdef HAS_snprintf_void
579     snprintf(next, state->size, format, a1, a2, a3, a4, a5, a6, a7, a8, a9,
580              a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20);
581     len = strlen(next);
582 #  else
583     len = snprintf(next, state->size, format, a1, a2, a3, a4, a5, a6, a7, a8,
584                    a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20);
585 #  endif
586 #endif
587 
588     /* check that printf() results fit in buffer */
589     if (len == 0 || len >= state->size || next[state->size - 1] != 0)
590         return 0;
591 
592     /* update buffer and position, compress first half if past that */
593     strm->avail_in += len;
594     state->x.pos += len;
595 
596     /* write out buffer if more than half is occupied */
597     ret = gz_vacate(state);
598     if (state->err && !state->again)
599         return state->err;
600     return (int)len;
601 #endif
602 }
603 
604 #endif
605 
606 /* -- see zlib.h -- */
607 int ZEXPORT gzflush(gzFile file, int flush) {
608     gz_statep state;
609 
610     /* get internal structure */
611     if (file == NULL)
612         return Z_STREAM_ERROR;
613     state = (gz_statep)file;
614 
615     /* check that we're writing and that there's no (serious) error */
616     if (state->mode != GZ_WRITE || (state->err != Z_OK && !state->again))
617         return Z_STREAM_ERROR;
618     gz_error(state, Z_OK, NULL);
619 
620     /* check flush parameter */
621     if (flush < 0 || flush > Z_FINISH)
622         return Z_STREAM_ERROR;
623 
624     /* check for seek request */
625     if (state->skip && gz_zero(state) == -1)
626         return state->err;
627 
628     /* compress remaining data with requested flush */
629     (void)gz_comp(state, flush);
630     return state->err;
631 }
632 
633 /* -- see zlib.h -- */
634 int ZEXPORT gzsetparams(gzFile file, int level, int strategy) {
635     gz_statep state;
636     z_streamp strm;
637 
638     /* get internal structure */
639     if (file == NULL)
640         return Z_STREAM_ERROR;
641     state = (gz_statep)file;
642     strm = &(state->strm);
643 
644     /* check that we're compressing and that there's no (serious) error */
645     if (state->mode != GZ_WRITE || (state->err != Z_OK && !state->again) ||
646             state->direct)
647         return Z_STREAM_ERROR;
648     gz_error(state, Z_OK, NULL);
649 
650     /* if no change is requested, then do nothing */
651     if (level == state->level && strategy == state->strategy)
652         return Z_OK;
653 
654     /* check for seek request */
655     if (state->skip && gz_zero(state) == -1)
656         return state->err;
657 
658     /* change compression parameters for subsequent input */
659     if (state->size) {
660         /* flush previous input with previous parameters before changing */
661         if (strm->avail_in && gz_comp(state, Z_BLOCK) == -1)
662             return state->err;
663         deflateParams(strm, level, strategy);
664     }
665     state->level = level;
666     state->strategy = strategy;
667     return Z_OK;
668 }
669 
670 /* -- see zlib.h -- */
671 int ZEXPORT gzclose_w(gzFile file) {
672     int ret = Z_OK;
673     gz_statep state;
674 
675     /* get internal structure */
676     if (file == NULL)
677         return Z_STREAM_ERROR;
678     state = (gz_statep)file;
679 
680     /* check that we're writing */
681     if (state->mode != GZ_WRITE)
682         return Z_STREAM_ERROR;
683 
684     /* check for seek request */
685     if (state->skip && gz_zero(state) == -1)
686         ret = state->err;
687 
688     /* flush, free memory, and close file */
689     if (gz_comp(state, Z_FINISH) == -1)
690         ret = state->err;
691     if (state->size) {
692         if (!state->direct) {
693             (void)deflateEnd(&(state->strm));
694             free(state->out);
695         }
696         free(state->in);
697     }
698     gz_error(state, Z_OK, NULL);
699     free(state->path);
700     if (close(state->fd) == -1)
701         ret = Z_ERRNO;
702     free(state);
703     return ret;
704 }
705