Imported Upstream version 1.02
[platform/upstream/libzio.git] / zio.c
1 /*
2  * zio.c        Provide an streamable interface to gziped/bzip2ed/LZW/LZMA files
3  *
4  * Copyright 2004 Werner Fink, 2004 SuSE LINUX AG, Germany.
5  * Copyright 2006 Werner Fink, 2006 SuSE Products GmbH, Germany.
6  * Copyright 2009 Werner Fink, 2009 SuSE Products GmbH, Germany.
7  * Copyright 2013 Werner Fink, 2013 SuSE Products GmbH, Germany.
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; version 2.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software Foundation,
20  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 
21  *
22  * Author:      Werner Fink <werner@suse.de>
23  */
24
25 #include "zioP.h"
26 #include "zio.h"
27 #include "lzw.h"
28
29 #if defined(HAS_ZLIB_H)
30 static ssize_t   zread(void *cookie, char *buf, size_t count)
31 {
32     if (!cookie) {
33         errno = EINVAL;
34         return -1;
35     }
36     return (ssize_t)gzread((gzFile)cookie, (voidp)buf, count);
37 }
38
39 static ssize_t   zwrite(void *cookie, const char *buf, size_t count)
40 {
41     if (!cookie) {
42         errno = EINVAL;
43         return -1;
44     }
45     return (ssize_t)gzwrite((gzFile)cookie, (const voidp)buf, count);
46 }
47
48 static zio_int_t zseek(void *cookie, zio_off_t *poffset, int whence)
49 {
50     if (!cookie) {
51         errno = EINVAL;
52         return -1;
53     }
54     off_t offset = (off_t)*poffset;
55     return (zio_int_t)gzseek((gzFile)cookie, (z_off_t)(*poffset), whence);
56 }
57
58 static int       zclose(void *cookie)
59 {
60     int status;
61     if (!cookie) {
62         errno = EINVAL;
63         return -1;
64     }
65     (void)gzflush((gzFile)cookie, Z_FINISH);
66     status = gzclose((gzFile)cookie);
67     return (status >= 0) ? 0 : EOF;
68 }
69
70 __extension__
71 static cookie_io_functions_t ioz = {
72     .read  = (cookie_read_function_t*) &zread,
73     .write = (cookie_write_function_t*)&zwrite,
74     .seek  = (cookie_seek_function_t*) &zseek,
75     .close = (cookie_close_function_t*)&zclose,
76 };
77 #else /* !HAS_ZLIB_H */
78 # error No support for `.gz' nor `.z' format
79 #endif /* !HAS_ZLIB_H */
80
81 #if defined(HAS_BZLIB_H)
82 # ifndef MIN
83 #  define MIN(x,y) ((x) < (y) ? (x) : (y))
84 # endif
85
86 typedef struct bzfile_s {
87     size_t position;
88     BZFILE *file;
89     FILE *stdio;
90     char *mode;
91     char *path;
92     int fd;
93 } bzfile_t;
94
95 static ssize_t   bzread(void *cookie, char *buf, size_t count)
96 {
97     bzfile_t *bzf = (bzfile_t*)cookie;
98     ssize_t len = -1;
99     if (!bzf)
100         goto out;
101     if (bzf->file)
102         len = (ssize_t)BZ2_bzread(bzf->file, (void*)buf, count);
103     if (len > 0)
104         bzf->position += len;
105 out:
106     if (len < 0)
107         errno = EINVAL;
108     return len;
109 }
110
111 static ssize_t   bzwrite(void *cookie, const char *buf, size_t count)
112 {
113     bzfile_t *bzf = (bzfile_t*)cookie;
114     ssize_t len = -1;
115     if (!bzf)
116         goto out;
117     if (bzf->file)
118         len = (ssize_t)BZ2_bzwrite(bzf->file, (void*)buf, count);
119     if (len > 0)
120         bzf->position += len;
121 out:
122     if (len < 0)
123         errno = EINVAL;
124     return len;
125 }
126
127 static zio_int_t bzseek(void *cookie, zio_off_t *poffset, int whence)
128 {
129     bzfile_t *bzf = (bzfile_t*)cookie;
130     off_t offset, curpos, newpos;
131     if (!bzf) {
132         errno = EINVAL;
133         return -1;
134     }
135
136     offset = (off_t)*poffset;
137     curpos = (off_t)bzf->position;
138
139     switch (whence) {
140     case SEEK_SET:
141         if (offset < 0) {
142             errno = EINVAL;
143             return -1;
144         }
145         newpos = offset;
146         break;
147     case SEEK_CUR:
148         if ((offset < 0 && (off_t)(-1 * offset) > curpos) || (offset > 0 && (offset+curpos) < curpos)) {
149             errno = EINVAL;
150             return -1;
151         }
152         newpos = curpos + offset;
153         break;
154     case SEEK_END:
155         newpos = -1;
156         break;
157     default:
158         errno = EINVAL;
159         return -1;
160     }
161
162     if (whence != SEEK_END && newpos < curpos) {
163         int status = BZ2_bzflush(bzf->file);
164         BZ2_bzclose(bzf->file);
165         if (status < 0) {
166             errno = EINVAL;
167             return -1;
168         }
169         if (bzf->fd >= 0) {
170             lseek(bzf->fd, 0, SEEK_SET);
171             bzf->file = BZ2_bzdopen(bzf->fd, bzf->mode);
172         } else if (bzf->path) {
173             bzf->file = BZ2_bzopen(bzf->path, bzf->mode);
174         } else {
175             errno = EINVAL;
176             return -1;
177         }
178         if (bzf->file == (BZFILE*)0) {
179             errno = EINVAL;
180             return -1;
181         }
182         curpos = (off_t)0;
183         bzf->position = curpos;
184     }
185     if (newpos == curpos)
186         goto out;
187     if (newpos == -1) {
188         char buf[1<<12];
189         while (1) {
190             ssize_t got_size = BZ2_bzread(bzf->file, buf, sizeof(buf));
191             if (got_size < 0)
192                 return -1;
193             if (got_size == 0)
194                 break;
195             curpos += got_size;
196         }
197     } else {
198         char buf[1<<12];
199         while (newpos > curpos) {
200             size_t  req_size = MIN(sizeof(buf), newpos - curpos);
201             ssize_t got_size = BZ2_bzread(bzf->file, buf, req_size);
202             if (got_size < 0)
203                 return -1;
204             if (got_size == 0)
205                 break;
206             curpos += got_size;
207         }
208     }
209 out:
210     bzf->position = curpos;
211     return curpos;
212 }
213
214 static int       bzclose(void *cookie)
215 {
216     bzfile_t *bzf = (bzfile_t*)cookie;
217     int status = -1;
218     if (!bzf) {
219         errno = EINVAL;
220         goto out;
221     }
222     if (bzf->file) {
223         status = BZ2_bzflush(bzf->file);
224         BZ2_bzclose(bzf->file);
225     }
226     free(cookie);
227 out:
228     return (status >= 0) ? 0 : EOF;
229 }
230
231 __extension__
232 static cookie_io_functions_t iobz = {
233     .read  = (cookie_read_function_t*) &bzread,
234     .write = (cookie_write_function_t*)&bzwrite,
235     .seek  = (cookie_seek_function_t*) &bzseek,
236     .close = (cookie_close_function_t*)&bzclose,
237 };
238 #else /* !HAS_BZLIB_H */
239 # warning No support for .bz2 format
240 #endif /* !HAS_BZLIB_H */
241
242 #if defined(HAS_LZMA_H)
243 # ifndef MIN
244 #  define MIN(x,y) ((x) < (y) ? (x) : (y))
245 # endif
246
247 typedef struct lzfile_s {
248     uint8_t buf[1<<12];
249     lzma_stream strm;
250     FILE *file;
251     FILE *stdio;
252     int encoding;
253     int level;
254     int what;
255     int eof;
256 } lzfile_t;
257
258 static lzma_ret lzmaopen(lzma_stream *__restrict strm, const char mode, const char what, int level)
259 {
260     lzma_ret ret;
261     if (mode == 'w') {
262         if (what == 'x')
263             ret = lzma_easy_encoder(strm, level, LZMA_CHECK_CRC32);
264         else {
265             lzma_options_lzma opt;
266             lzma_lzma_preset(&opt, level);
267             ret = lzma_alone_encoder(strm, &opt);
268         }
269    } else
270         ret = lzma_auto_decoder(strm, 100<<20, 0);
271    return ret;
272 }
273
274 static ssize_t   lzmaread(void *cookie, char *buf, size_t count)
275 {
276     lzfile_t *lzma = (lzfile_t*)cookie;
277     lzma_stream *strm;
278     ssize_t eof = 0;
279     if (!lzma || lzma->encoding)
280         return -1;
281     if (lzma->eof)
282         return 0;
283     strm = &lzma->strm;
284
285     strm->next_out = (uint8_t*)buf;
286     strm->avail_out = count;
287     while (1) {
288         lzma_ret lret;
289         if (!strm->avail_in) {
290             strm->next_in = lzma->buf;
291             strm->avail_in = fread(lzma->buf, 1, sizeof(lzma->buf), lzma->file);
292             if (strm->avail_in == 0)
293                 eof = 1;
294         }
295         lret = lzma_code(strm, LZMA_RUN);
296         if (lret == LZMA_STREAM_END) {
297             lzma->eof = 1;
298             return count - strm->avail_out;
299         }
300         if (lret != LZMA_OK)
301             return -1;
302         if (strm->avail_out == 0)
303             return count;
304         if (eof) {
305             eof = feof(lzma->file);
306             clearerr(lzma->file);
307             if (eof)
308                 break;
309             return -1;
310         }
311     }
312     return 0;
313 }
314
315 static ssize_t   lzmawrite(void *cookie, const char *buf, size_t count)
316 {
317     lzfile_t *lzma = (lzfile_t*)cookie;
318     lzma_stream *strm;
319     if (!lzma || !lzma->encoding)
320     return -1;
321     if (!count)
322     return 0;
323     strm = &lzma->strm;
324
325     strm->next_in = (uint8_t*)buf;
326     strm->avail_in = count;
327     while (1) {
328         lzma_ret lret;
329         size_t len;
330         strm->next_out = lzma->buf;
331         strm->avail_out = sizeof(lzma->buf);
332         lret = lzma_code(strm, LZMA_RUN);
333         if (lret != LZMA_OK)
334             break;
335         len = sizeof(lzma->buf) - strm->avail_out;
336         if (len && fwrite(lzma->buf, 1, len, lzma->file) != len)
337             break;
338         if (strm->avail_in == 0)
339             return len;
340     }
341     return -1;
342 }
343
344 static zio_int_t lzmaseek(void *cookie, zio_off_t *poffset, int whence)
345 {
346     lzfile_t *lzma = (lzfile_t*)cookie;
347     lzma_stream *strm;
348     off_t offset, curpos, newpos;
349     if (!lzma) {
350         errno = EINVAL;
351         return -1;
352     }
353
354     strm = &lzma->strm;
355     offset = (off_t)*poffset;
356     curpos = (off_t)strm->total_out;
357
358     switch (whence) {
359     case SEEK_SET:
360         if (offset < 0) {
361             errno = EINVAL;
362             return -1;
363         }
364         newpos = offset;
365         break;
366     case SEEK_CUR:
367         if ((offset < 0 && (off_t)(-1 * offset) > curpos) || (offset > 0 && (offset+curpos) < curpos)) {
368             errno = EINVAL;
369             return -1;
370         }
371         newpos = curpos + offset;
372         break;
373     case SEEK_END:
374         newpos = -1;
375         break;
376     default:
377         errno = EINVAL;
378         return -1;
379     }
380     if (whence != SEEK_END && newpos < curpos) {
381         lzma_ret ret;
382
383         lzma_end(strm);
384         lzma->strm = (lzma_stream)LZMA_STREAM_INIT;
385         strm = &lzma->strm;
386
387         rewind(lzma->file);
388         clearerr(lzma->file);
389         lzma->eof = 0;
390
391         ret = lzmaopen(strm, lzma->encoding ? 'w' : 'r', lzma->what, lzma->level);
392         if (ret != LZMA_OK || strm->total_out != 0) {
393             fclose(lzma->file);
394             errno = EINVAL;
395             return -1;
396         }
397         curpos = (off_t)0;
398     }
399     if (newpos == curpos)
400         goto out;
401     if (newpos == -1) {
402         char buf[sizeof(lzma->buf)];
403         while (1) {
404             ssize_t got_size = lzmaread(cookie, buf, sizeof(buf));
405             if (got_size < 0)
406                 return -1;
407             if (got_size == 0)
408                 break;
409             curpos += got_size;
410         }
411     } else {
412         char buf[sizeof(lzma->buf)];
413         while (newpos > curpos) {
414             size_t  req_size = MIN(sizeof(buf), newpos - curpos);
415             ssize_t got_size = lzmaread(cookie, buf, req_size);
416             if (got_size < 0)
417                 return -1;
418             if (got_size == 0)
419                 break;
420             curpos += got_size;
421         }
422     }
423 out:
424     return curpos;
425 }
426
427 static int       lzmaclose(void *cookie)
428 {
429     lzfile_t *lzma = (lzfile_t*)cookie;
430     lzma_ret lret = LZMA_STREAM_END;
431     lzma_stream *strm;
432     int fret = -1;
433     if (!lzma)
434         return -1;
435     if (!lzma->encoding)
436         goto out;
437     strm = &lzma->strm;
438     while (1) {
439         size_t len;
440         strm->avail_out = sizeof(lzma->buf);
441         strm->next_out = (uint8_t*)lzma->buf;
442         lret = lzma_code(strm, LZMA_FINISH);
443         if (lret != LZMA_OK && lret != LZMA_STREAM_END)
444             goto out;
445         len = sizeof(lzma->buf) - strm->avail_out;
446         if (len && fwrite(lzma->buf, 1, len, lzma->file) != len)
447             goto out;
448         if (lret == LZMA_STREAM_END)
449             break;
450     }
451     lzma_end(strm);
452 out:
453     fret = fclose(lzma->file);
454     free(lzma);
455     if (lret != LZMA_STREAM_END)
456         fret = -1;
457     return fret;
458 }
459
460 __extension__
461 static cookie_io_functions_t iolzma = {
462     .read  = (cookie_read_function_t*) &lzmaread,
463     .write = (cookie_write_function_t*)&lzmawrite,
464     .seek  = (cookie_seek_function_t*) &lzmaseek,
465     .close = (cookie_close_function_t*)&lzmaclose,
466 };
467 #else /* !HAS_LZMA_H */
468 # if defined(HAS_LZMADEC_H)
469 static ssize_t   lzmaread(void *cookie, char *buf, size_t count)
470 {
471     if (!cookie) {
472         errno = EINVAL;
473         return -1;
474     }
475     return lzmadec_read((lzmadec_FILE*)cookie, (uint8_t*)buf, count);
476 }
477
478 static ssize_t   lzmawrite(void *cookie, const char *buf, size_t count)
479 {
480     errno = ENOTSUP;
481     return -1;
482 }
483
484 static zio_int_t lzmaseek(void *cookie, zio_off_t *poffset, int whence)
485 {
486     if (!cookie) {
487         errno = EINVAL;
488         return -1;
489     }
490     return (zio_int_t)lzmadec_seek((lzmadec_FILE*)cookie, (off_t)(*poffset), whence);
491 }
492
493 static int       lzmaclose(void *cookie)
494 {
495     if (!cookie) {
496         errno = EINVAL;
497         return -1;
498     }
499     int_fast8_t status = lzmadec_close((lzmadec_FILE*)cookie);
500     return (status >= 0) ? 0 : EOF;
501 }
502
503 __extension__
504 static cookie_io_functions_t iolzma = {
505     .read  = (cookie_read_function_t*) &lzmaread,
506     .write = (cookie_write_function_t*)&lzmawrite,
507     .seek  = (cookie_seek_function_t*) &lzmaseek,
508     .close = (cookie_close_function_t*)&lzmaclose,
509 };
510 # else /* !HAS_LZMADEC_H */
511 #  warning No support for .lzma format
512 # endif /* !HAS_LZMADEC_H */
513 #endif /* !HAS_LZMA_H */
514
515 typedef struct lzwfile_s {
516     size_t position;
517     LZW_t *file;
518     FILE *stdio;
519     char *mode;
520     char *path;
521     int fd;
522 } lzwfile_t;
523
524 static ssize_t   lzwread(void *cookie, char *buf, size_t count)
525 {
526     lzwfile_t *lzw = (lzwfile_t*)cookie;
527     ssize_t len = -1;
528     if (!lzw)
529         goto out;
530     if (lzw->file)
531         len = readlzw(lzw->file, buf, count);
532     if (len > 0)
533         lzw->position += len;
534 out:
535     if (len < 0)
536         errno = EINVAL;
537     return len;
538 }
539
540 static ssize_t   lzwwrite(void *cookie, const char *buf, size_t count)
541 {
542     errno = ENOTSUP;
543     return -1;
544 }
545
546 static zio_int_t lzwseek(void *cookie, zio_off_t *poffset, int whence)
547 {
548     lzwfile_t *lzw = (lzwfile_t*)cookie;
549     off_t offset, curpos, newpos;
550     if (!lzw) {
551         errno = EINVAL;
552         return -1;
553     }
554
555     offset = (off_t)*poffset;
556     curpos = (off_t)lzw->position;
557
558     switch (whence) {
559     case SEEK_SET:
560         if (offset < 0) {
561             errno = EINVAL;
562             return -1;
563         }
564         newpos = offset;
565         break;
566     case SEEK_CUR:
567         if ((offset < 0 && (off_t)(-1 * offset) > curpos) || (offset > 0 && (offset+curpos) < curpos)) {
568             errno = EINVAL;
569             return -1;
570         }
571         newpos = curpos + offset;
572         break;
573     case SEEK_END:
574         newpos = -1;
575         break;
576     default:
577         errno = EINVAL;
578         return -1;
579     }
580
581     if (whence != SEEK_END && newpos < curpos) {
582         closelzw(lzw->file);
583         if (lzw->fd >= 0) {
584             lseek(lzw->fd, 0, SEEK_SET);
585             lzw->file = dopenlzw(lzw->fd, lzw->mode);
586         } else if (lzw->path) {
587             lzw->file = openlzw(lzw->path, lzw->mode);
588         } else {
589             errno = EINVAL;
590             return -1;
591         }
592         if (lzw->file == (LZW_t*)0) {
593             errno = EINVAL;
594             return -1;
595         }
596         curpos = (off_t)0;
597         lzw->position = curpos;
598     }
599     if (newpos == curpos)
600         goto out;
601     if (newpos == -1) {
602         char buf[1<<12];
603         while (1) {
604             ssize_t got_size = readlzw(lzw->file, buf, sizeof(buf));
605             if (got_size < 0)
606                 return -1;
607             if (got_size == 0)
608                 break;
609             curpos += got_size;
610         }
611     } else {
612         char buf[1<<12];
613         while (newpos > curpos) {
614             size_t  req_size = MIN(sizeof(buf), newpos - curpos);
615             ssize_t got_size = readlzw(lzw->file, buf, req_size);
616             if (got_size < 0)
617                 return -1;
618             if (got_size == 0)
619                 break;
620             curpos += got_size;
621         }
622     }
623 out:
624     lzw->position = curpos;
625     return curpos;
626 }
627
628 static int       lzwclose(void *cookie)
629 {
630     lzwfile_t *lzw = (lzwfile_t*)cookie;
631     if (!lzw) {
632         errno = EINVAL;
633         return -1;
634     }
635     if (lzw->file)
636         closelzw(lzw->file);
637     free(cookie);
638     return 0;
639 }
640
641 __extension__
642 static cookie_io_functions_t iolzw = {
643     .read  = (cookie_read_function_t*) &lzwread,
644     .write = (cookie_write_function_t*)&lzwwrite,
645     .seek  = (cookie_seek_function_t*) &lzwseek,
646     .close = (cookie_close_function_t*)&lzwclose,
647 };
648
649 static inline char autodetect(char **__restrict path, const char *__restrict check)
650 {
651     const size_t len = strlen(*path);
652     char *suff = strrchr(*path, '.');
653     char *ext = *path;
654     char what = 'n';
655
656     if (suff) {
657         suff++;
658         if      (strcmp(suff, "z"   ) == 0)
659             what = 'z';
660         else if (strcmp(suff, "gz"  ) == 0)
661             what = 'g';
662         else if (strcmp(suff, "Z"   ) == 0)
663             what = 'Z';
664         else if (strcmp(suff, "bz2" ) == 0)
665             what = 'b';
666         else if (strcmp(suff, "lzma") == 0)
667             what = 'l';
668         else if (strcmp(suff, "xz"  ) == 0)
669             what = 'x';
670     }
671
672     if (what == 'n' && *check == 'r') {
673         int olderr, fd;
674         struct stat st;
675         char m[5];
676         ext = malloc(sizeof(char)*(len + 5 + 1));
677         if (!ext)
678             goto out;
679         strcpy(ext, *path);
680         suff = (ext+len);
681
682         olderr = errno;
683         if (stat(strcat(ext, ".gz"),  &st) == 0) {
684             what = 'g';
685             goto skip;
686         }
687         *suff = '\0';
688         if (stat(strcat(ext, ".bz2"), &st) == 0) {
689             what = 'b';
690             goto skip;
691         }
692         *suff = '\0';
693         if (stat(strcat(ext, ".z"),   &st) == 0) {
694             what = 'z';
695             goto skip;
696         }
697         *suff = '\0';
698         if (stat(strcat(ext, ".Z"),   &st) == 0) {
699             what = 'Z';
700             goto skip;
701         }
702         *suff = '\0';
703         if (stat(strcat(ext, ".lzma"), &st) == 0) {
704             what = 'l';
705             goto skip;
706         }
707         *suff = '\0';
708         if (stat(strcat(ext, ".xz"), &st) == 0) {
709             what = 'x';
710             goto skip;
711         }
712         *suff = '\0';
713
714         if ((fd = open(ext, O_RDONLY|O_NOCTTY)) < 0)
715             goto skip;
716         if (read(fd, m, sizeof(m)) == sizeof(m)) {
717             if (m[0] == '\037' && m[1] == '\213')
718                 what = 'g';
719             if (m[0] == '\037' && m[1] == '\235')
720                 what = 'Z';
721             if (m[0] == '\037' && m[1] == '\236')
722                 what = 'z';
723             else if (m[0] == 'B' && m[1] == 'Z' && m[2] == 'h')
724                 what = 'b';
725             else if (m[0] == ']' && m[1] == '\0' && m[2] == '\0' && m[3] == '\200') /* weak!! */
726                 what = 'l';
727             else if (m[0] == '\377' && m[1] == 'L' && m[2] == 'Z' && m[3] == 'M' && m[4] == 'A')
728                 what = 'l';
729             else if (m[0] == '\375' && m[1] == '7' && m[2] == 'z' && m[3] == 'X' && m[4] == 'Z')
730                 what = 'x';
731         }
732         close(fd);
733     skip:
734         errno = olderr;
735     }
736 out:
737     *path = ext;
738     return what;
739 }
740
741 FILE * fzopen(const char * path, const char * mode)
742 {
743     FILE * ret = (FILE *)0;
744     char * check = (char*)0, * ext = (char*)0;
745     size_t n = 0, len;
746     unsigned int i;
747     char what = 'n';
748
749     if (!mode || !(n = strlen(mode))) {
750         errno = EINVAL;
751         goto out;
752     }
753
754     if (!(check = (char*)malloc(n*sizeof(char))))
755         goto out;
756
757     /* No append mode possible */
758     switch (*mode) {
759         case 'r': check[0] = 'r'; break;
760         case 'w': check[0] = 'w'; break;
761         default:  errno = EINVAL; goto out;
762     }
763
764     for (i = 1; i < n; i++) {
765         /* We can only open for reading OR writing but NOT for both */
766         switch (mode[i]) {
767             case '\0': break;
768             case '+': errno = EINVAL; goto out;
769             case 'b': case 'x': check[i] = mode[i]; continue;
770             /* Ingore switches for gzopen() */
771             case 'f': case 'h': check[i] = '\0';    continue;
772             default:            check[i] = '\0';    continue;
773         }
774         break;
775     }
776
777     if (!path || !(len = strlen(path))) {
778         errno = EINVAL;
779         goto out;
780     }
781
782     ext = (char *)path;
783     what = autodetect(&ext, check);
784
785     switch (what) {
786     case 'g':
787     case 'z':           /* Is this correct? Old gzip magic */
788 #if defined(HAS_ZLIB_H)
789         {
790             gzFile cookie;
791
792             if (&gzopen == NULL) {
793                 errno = ENOSYS;
794                 goto out;
795             }
796
797             if (!(cookie = gzopen(ext, mode))) {
798                 if (!errno)
799                     errno = ENOMEM;
800                 goto out;
801             }
802
803             if (!(ret = fopencookie((void*)cookie, check, ioz))) {
804                 gzclose(cookie);
805                 errno = EINVAL;
806                 goto out;
807             }
808 # ifndef LIBIO_IS_FIXED
809             if (ret->_fileno < 0)
810                 ret->_fileno = 0;
811 # endif
812         }
813 #else /* !HAS_ZLIB_H */
814         errno = ENOTSUP;
815 #endif /* !HAS_ZLIB_H */
816         break;
817     case 'Z':
818         {
819             lzwfile_t *__restrict cookie;
820             if (*mode != 'r') {
821                 errno = ENOTSUP;
822                 goto out;
823             }
824
825             if (posix_memalign((void*)&cookie, sizeof(void*), alignof(lzwfile_t)+strsize(ext)+strsize(mode)) != 0)
826                 goto out;
827             memset(cookie, 0, alignof(lzwfile_t)+strsize(ext)+strsize(mode));
828
829             cookie->fd = -1;
830             cookie->mode = ((char*)cookie)+alignof(lzwfile_t);
831             cookie->path = cookie->mode + strsize(mode);
832             strcpy(cookie->mode, mode);
833             strcpy(cookie->path, ext);
834
835             if (!(cookie->file = openlzw(ext, mode))) {
836                 free(cookie);
837                 if (!errno)
838                     errno = ENOMEM;
839                 goto out;
840             }
841
842             if (!(ret = fopencookie((void*)cookie, check, iolzw))) {
843                 closelzw(cookie->file);
844                 free(cookie);
845                 errno = EINVAL;
846                 goto out;
847             }
848 # ifndef LIBIO_IS_FIXED
849             if (ret->_fileno < 0)
850                 ret->_fileno = 0;
851 # endif
852             cookie->stdio = ret;
853         }
854         break;
855     case 'b':
856 #if defined(HAS_BZLIB_H)
857         {
858             bzfile_t *__restrict cookie;
859             int level = 5;
860
861             if (&BZ2_bzopen == NULL) {
862                 errno = ENOSYS;
863                 goto out;
864             }
865
866             if (posix_memalign((void*)&cookie, sizeof(void*), alignof(bzfile_t)+strsize(ext)+strsize(mode)) != 0)
867                 goto out;
868             memset(cookie, 0, alignof(bzfile_t)+strsize(ext)+strsize(mode));
869
870             for (i = 1; i < n; i++) {
871                 if (mode[i] >= '0' && mode[i] <= '9') {
872                     level = (int)mode[i];
873                     break;
874                 }
875             }
876
877             cookie->fd = -1;
878             cookie->mode = ((char*)cookie)+alignof(bzfile_t);
879             cookie->path = cookie->mode+strsize(mode);
880             strcpy(cookie->mode, mode);
881             strcpy(cookie->path, ext);
882
883             if (!(cookie->file = BZ2_bzopen(ext, mode))) {
884                 free(cookie);
885                 if (!errno)
886                     errno = ENOMEM;
887                 goto out;
888             }
889
890             if (!(ret = fopencookie((void*)cookie, check, iobz))) {
891                 BZ2_bzclose(cookie->file);
892                 free(cookie);
893                 errno = EINVAL;
894                 goto out;
895             }
896 # ifndef LIBIO_IS_FIXED
897             if (ret->_fileno < 0)
898                 ret->_fileno = 0;
899 # endif
900             cookie->stdio = ret;
901         }
902 #else /* !HAS_BZLIB_H */
903         errno = ENOTSUP;
904 #endif /* !HAS_BZLIB_H */
905         break;
906 #if defined(HAS_LZMA_H)
907     case 'l':
908     case 'x':
909         {
910             int level = LZMA_PRESET_DEFAULT;
911             lzfile_t *__restrict cookie;
912             lzma_ret lret;
913
914             if (&lzma_auto_decoder == NULL) {
915                 errno = ENOSYS;
916                 goto out;
917             }
918
919             if (posix_memalign((void*)&cookie, sizeof(void*), alignof(lzfile_t)) != 0)
920                 goto out;
921             memset(cookie, 0, alignof(lzfile_t));
922
923             if ((cookie->file = fopen(ext, check)) == NULL) {
924                 free(cookie);
925                 goto out;
926             }
927
928             for (i = 1; i < n; i++) {
929                 if (mode[i] >= '0' && mode[i] <= '9') {
930                     level = (int)mode[i];
931                     break;
932                 }
933             }
934
935             cookie->eof = 0;
936             cookie->what = what;
937             cookie->strm = (lzma_stream)LZMA_STREAM_INIT;
938             cookie->level = level;
939             cookie->encoding = (check[0] == 'w') ? 1 : 0;
940             lret = lzmaopen(&cookie->strm, check[0], what, level);
941
942             if (lret != LZMA_OK) {
943                 fclose(cookie->file);
944                 free(cookie);
945                 errno = EINVAL;
946                 goto out;
947             }
948             if (!(ret = fopencookie((void*)cookie, check, iolzma))) {
949                 lzma_end(&cookie->strm);
950                 fclose(cookie->file);
951                 free(cookie);
952                 errno = EINVAL;
953                 goto out;
954             }
955 #  ifndef LIBIO_IS_FIXED
956             if (ret->_fileno < 0)
957                 ret->_fileno = 0;
958 #  endif
959             cookie->stdio = ret;
960         }
961         break;
962 #else /* !HAS_LZMA_H */
963     case 'x':
964         errno = ENOTSUP;
965         break;
966     case 'l':
967 # if defined(HAS_LZMADEC_H)
968         {
969             lzmadec_FILE* cookie;
970
971             if (*mode != 'r') {
972                 errno = ENOTSUP;
973                 goto out;
974             }
975
976             if (&lzmadec_open == NULL) {
977                 errno = ENOSYS;
978                 goto out;
979             }
980
981             if (!(cookie = lzmadec_open(ext))) {
982                 if (!errno)
983                     errno = ENOMEM;
984                 goto out;
985             } 
986
987             if (!(ret = fopencookie((void*)cookie, check, iolzma))) {
988                 lzmadec_close(cookie);
989                 errno = EINVAL;
990                 goto out;
991             }
992 #  ifndef LIBIO_IS_FIXED
993             if (ret->_fileno < 0)
994                 ret->_fileno = 0;
995 #  endif
996             cookie->stdio = ret;
997         }
998 # else /* !HAS_LZMADEC_H */
999         errno = ENOTSUP;
1000 # endif /* !HAS_LZMADEC_H */
1001 #endif /* !HAS_LZMA_H */
1002         break;
1003     default:
1004         ret = fopen(ext, mode);
1005         break;
1006     }
1007
1008 out:
1009     if (check)
1010         free(check);
1011     if (ext && ext != path)
1012         free(ext);
1013
1014     return ret;
1015 }
1016
1017 FILE * fdzopen(int fildes, const char * mode, const char *what)
1018 {
1019     FILE * ret = (FILE *)0;
1020     char * check = (char*)0;
1021     size_t n = 0;
1022     unsigned int i;
1023
1024     if (!mode || !(n = strlen(mode))) {
1025         errno = EINVAL;
1026         goto out;
1027     }
1028
1029     if (!(check = (char*)malloc(n*sizeof(char))))
1030         goto out;
1031
1032     /* No append mode possible */
1033     switch (*mode) {
1034         case 'r': check[0] = 'r'; break;
1035         case 'w': check[0] = 'w'; break;
1036         default:  errno = EINVAL; goto out;
1037     }
1038
1039     for (i = 1; i < n; i++) {
1040         /* We can only open for reading OR writing but NOT for both */
1041         switch (mode[i]) {
1042             case '\0': break;
1043             case '+': errno = EINVAL; goto out;
1044             case 'b': case 'x': check[i] = mode[i]; continue;
1045             /* Ingore switches for gzopen() */
1046             case 'f': case 'h': check[i] = '\0';    continue;
1047             default:            check[i] = '\0';    continue;
1048         }
1049         break;
1050     }
1051
1052     switch (*what) {
1053     case 'g':
1054     case 'z':           /* Is this correct? Old gzip magic */
1055 #if defined(HAS_ZLIB_H)
1056         {
1057             gzFile cookie;
1058
1059             if (&gzdopen == NULL) {
1060                 errno = ENOSYS;
1061                 goto out;
1062             }
1063
1064             if (!(cookie = gzdopen(fildes, mode))) {
1065                 if (!errno)
1066                     errno = ENOMEM;
1067                 goto out;
1068             }
1069
1070             if (!(ret = fopencookie((void*)cookie, check, ioz))) {
1071                 gzclose(cookie);
1072                 errno = EINVAL;
1073                 goto out;
1074             }
1075 # ifndef LIBIO_IS_FIXED
1076             if (ret->_fileno < 0)
1077                 ret->_fileno = 0;
1078 # endif
1079         }
1080 #else /* !HAS_ZLIB_H */
1081         errno = ENOTSUP;
1082 #endif /* !HAS_ZLIB_H */
1083         break;
1084     case 'Z':
1085         {
1086             lzwfile_t *__restrict cookie;
1087             if (*mode != 'r') {
1088                 errno = ENOTSUP;
1089                 goto out;
1090             }
1091
1092             if (posix_memalign((void*)&cookie, sizeof(void*), alignof(lzwfile_t)+strsize(mode)) != 0)
1093                 goto out;
1094             memset(cookie, 0, alignof(lzwfile_t)+strsize(mode));
1095
1096             cookie->fd = fildes;
1097             cookie->mode = ((char*)cookie)+alignof(lzwfile_t);
1098             strcpy(cookie->mode, mode);
1099
1100             if (!(cookie->file = dopenlzw(fildes, mode))) {
1101                 free(cookie);
1102                 if (!errno)
1103                     errno = ENOMEM;
1104                 goto out;
1105             }
1106
1107             if (!(ret = fopencookie((void*)cookie, check, iolzw))) {
1108                 closelzw(cookie->file);
1109                 free(cookie);
1110                 errno = EINVAL;
1111                 goto out;
1112             }
1113 # ifndef LIBIO_IS_FIXED
1114             if (ret->_fileno < 0)
1115                 ret->_fileno = 0;
1116 # endif
1117             cookie->stdio = ret;
1118         }
1119         break;
1120     case 'b':
1121 #if defined(HAS_BZLIB_H)
1122         {
1123             bzfile_t *__restrict cookie;
1124             int level = 5;
1125
1126             if (&BZ2_bzdopen == NULL) {
1127                 errno = ENOSYS;
1128                 goto out;
1129             }
1130
1131             if (posix_memalign((void*)&cookie, sizeof(void*), alignof(bzfile_t)+strsize(mode)) != 0)
1132                 goto out;
1133             memset(cookie, 0, alignof(bzfile_t)+strsize(mode));
1134
1135             for (i = 1; i < n; i++) {
1136                 if (mode[i] >= '0' && mode[i] <= '9') {
1137                     level = (int)mode[i];
1138                     break;
1139                 }
1140             }
1141
1142             cookie->fd = fildes;
1143             cookie->mode = ((char*)cookie)+alignof(bzfile_t);
1144             strcpy(cookie->mode, mode);
1145
1146             if (cookie->mode == (char*)0) {
1147                 free(cookie);
1148                 goto out;
1149             }
1150             if (!(cookie->file = BZ2_bzdopen(fildes, mode))) {
1151                 free(cookie);
1152                 if (!errno)
1153                     errno = ENOMEM;
1154                 goto out;
1155             }
1156
1157             if (!(ret = fopencookie((void*)cookie, check, iobz))) {
1158                 BZ2_bzclose(cookie->file);
1159                 free(cookie);
1160                 errno = EINVAL;
1161                 goto out;
1162             }
1163 # ifndef LIBIO_IS_FIXED
1164             if (ret->_fileno < 0)
1165                 ret->_fileno = 0;
1166 # endif
1167             cookie->stdio = ret;
1168         }
1169 #else /* !HAS_BZLIB_H */
1170         errno = ENOTSUP;
1171 #endif /* !HAS_BZLIB_H */
1172         break;
1173 #if defined(HAS_LZMA_H)
1174     case 'l':
1175     case 'x':
1176         {
1177             int level = LZMA_PRESET_DEFAULT;
1178             lzfile_t *__restrict cookie;
1179             lzma_ret lret;
1180
1181             if (&lzma_auto_decoder == NULL) {
1182                 errno = ENOSYS;
1183                 goto out;
1184             }
1185
1186             if (posix_memalign((void*)&cookie, sizeof(void*), alignof(lzfile_t)) != 0)
1187                 goto out;
1188             memset(cookie, 0, alignof(lzfile_t));
1189
1190             if ((cookie->file = fdopen(fildes, check)) == NULL) {
1191                 free(cookie);
1192                 goto out;
1193             }
1194
1195             for (i = 1; i < n; i++) {
1196                 if (mode[i] >= '0' && mode[i] <= '9') {
1197                     level = (int)mode[i];
1198                     break;
1199                 }
1200             }
1201
1202             cookie->eof = 0;
1203             cookie->what = *what;
1204             cookie->strm = (lzma_stream)LZMA_STREAM_INIT;
1205             cookie->level = level;
1206             cookie->encoding = (check[0] == 'w') ? 1 : 0;
1207             lret = lzmaopen(&cookie->strm, check[0], *what, level);
1208
1209             if (lret != LZMA_OK) {
1210                 fclose(cookie->file);
1211                 free(cookie);
1212                 errno = EINVAL;
1213                 goto out;
1214             }
1215             if (!(ret = fopencookie((void*)cookie, check, iolzma))) {
1216                 lzma_end(&cookie->strm);
1217                 fclose(cookie->file);
1218                 free(cookie);
1219                 errno = EINVAL;
1220                 goto out;
1221             }
1222 #  ifndef LIBIO_IS_FIXED
1223             if (ret->_fileno < 0)
1224                 ret->_fileno = 0;
1225 #  endif
1226             cookie->stdio = ret;
1227         }
1228         break;
1229 #else /* !HAS_LZMA_H */
1230     case 'x':
1231         errno = ENOTSUP;
1232         break;
1233     case 'l':
1234 # if defined(HAS_LZMADEC_H)
1235         {
1236             lzmadec_FILE* cookie;
1237
1238             if (*mode != 'r') {
1239                 errno = ENOTSUP;
1240                 goto out;
1241             }
1242
1243             if (&lzmadec_open == NULL) {
1244                 errno = ENOSYS;
1245                 goto out;
1246             }
1247
1248             if (!(cookie = lzmadec_dopen(fildes))) {
1249                 if (!errno)
1250                     errno = ENOMEM;
1251                 goto out;
1252             } 
1253
1254             if (!(ret = fopencookie((void*)cookie, check, iolzma))) {
1255                 lzmadec_close(cookie);
1256                 errno = EINVAL;
1257                 goto out;
1258             }
1259 #  ifndef LIBIO_IS_FIXED
1260             if (ret->_fileno < 0)
1261                 ret->_fileno = 0;
1262 #  endif
1263             cookie->stdio = ret;
1264         }
1265 # else /* !HAS_LZMADEC_H */
1266         errno = ENOTSUP;
1267 # endif /* !HAS_LZMADEC_H */
1268 #endif /* !HAS_LZMA_H */
1269         break;
1270     default:
1271         ret = fdopen(fildes, mode);
1272         break;
1273     }
1274
1275 out:
1276     if (check)
1277         free(check);
1278     return ret;
1279 }