2 * zio.c Provide an streamable interface to gziped/bzip2ed/LZW/LZMA files
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.
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.
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.
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.
22 * Author: Werner Fink <werner@suse.de>
29 #if defined(HAS_ZLIB_H)
30 static ssize_t zread(void *cookie, char *buf, size_t count)
36 return (ssize_t)gzread((gzFile)cookie, (voidp)buf, count);
39 static ssize_t zwrite(void *cookie, const char *buf, size_t count)
45 return (ssize_t)gzwrite((gzFile)cookie, (const voidp)buf, count);
48 static zio_int_t zseek(void *cookie, zio_off_t *poffset, int whence)
54 off_t offset = (off_t)*poffset;
55 return (zio_int_t)gzseek((gzFile)cookie, (z_off_t)(*poffset), whence);
58 static int zclose(void *cookie)
65 (void)gzflush((gzFile)cookie, Z_FINISH);
66 status = gzclose((gzFile)cookie);
67 return (status >= 0) ? 0 : EOF;
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,
77 #else /* !HAS_ZLIB_H */
78 # error No support for `.gz' nor `.z' format
79 #endif /* !HAS_ZLIB_H */
81 #if defined(HAS_BZLIB_H)
83 # define MIN(x,y) ((x) < (y) ? (x) : (y))
86 typedef struct bzfile_s {
95 static ssize_t bzread(void *cookie, char *buf, size_t count)
97 bzfile_t *bzf = (bzfile_t*)cookie;
102 len = (ssize_t)BZ2_bzread(bzf->file, (void*)buf, count);
104 bzf->position += len;
111 static ssize_t bzwrite(void *cookie, const char *buf, size_t count)
113 bzfile_t *bzf = (bzfile_t*)cookie;
118 len = (ssize_t)BZ2_bzwrite(bzf->file, (void*)buf, count);
120 bzf->position += len;
127 static zio_int_t bzseek(void *cookie, zio_off_t *poffset, int whence)
129 bzfile_t *bzf = (bzfile_t*)cookie;
130 off_t offset, curpos, newpos;
136 offset = (off_t)*poffset;
137 curpos = (off_t)bzf->position;
148 if ((offset < 0 && (off_t)(-1 * offset) > curpos) || (offset > 0 && (offset+curpos) < curpos)) {
152 newpos = curpos + offset;
162 if (whence != SEEK_END && newpos < curpos) {
163 int status = BZ2_bzflush(bzf->file);
164 BZ2_bzclose(bzf->file);
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);
178 if (bzf->file == (BZFILE*)0) {
183 bzf->position = curpos;
185 if (newpos == curpos)
190 ssize_t got_size = BZ2_bzread(bzf->file, buf, sizeof(buf));
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);
210 bzf->position = curpos;
214 static int bzclose(void *cookie)
216 bzfile_t *bzf = (bzfile_t*)cookie;
223 status = BZ2_bzflush(bzf->file);
224 BZ2_bzclose(bzf->file);
228 return (status >= 0) ? 0 : EOF;
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,
238 #else /* !HAS_BZLIB_H */
239 # warning No support for .bz2 format
240 #endif /* !HAS_BZLIB_H */
242 #if defined(HAS_LZMA_H)
244 # define MIN(x,y) ((x) < (y) ? (x) : (y))
247 typedef struct lzfile_s {
258 static lzma_ret lzmaopen(lzma_stream *__restrict strm, const char mode, const char what, int level)
263 ret = lzma_easy_encoder(strm, level, LZMA_CHECK_CRC32);
265 lzma_options_lzma opt;
266 lzma_lzma_preset(&opt, level);
267 ret = lzma_alone_encoder(strm, &opt);
270 ret = lzma_auto_decoder(strm, 100<<20, 0);
274 static ssize_t lzmaread(void *cookie, char *buf, size_t count)
276 lzfile_t *lzma = (lzfile_t*)cookie;
279 if (!lzma || lzma->encoding)
285 strm->next_out = (uint8_t*)buf;
286 strm->avail_out = count;
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)
295 lret = lzma_code(strm, LZMA_RUN);
296 if (lret == LZMA_STREAM_END) {
298 return count - strm->avail_out;
302 if (strm->avail_out == 0)
305 eof = feof(lzma->file);
306 clearerr(lzma->file);
315 static ssize_t lzmawrite(void *cookie, const char *buf, size_t count)
317 lzfile_t *lzma = (lzfile_t*)cookie;
319 if (!lzma || !lzma->encoding)
325 strm->next_in = (uint8_t*)buf;
326 strm->avail_in = count;
330 strm->next_out = lzma->buf;
331 strm->avail_out = sizeof(lzma->buf);
332 lret = lzma_code(strm, LZMA_RUN);
335 len = sizeof(lzma->buf) - strm->avail_out;
336 if (len && fwrite(lzma->buf, 1, len, lzma->file) != len)
338 if (strm->avail_in == 0)
344 static zio_int_t lzmaseek(void *cookie, zio_off_t *poffset, int whence)
346 lzfile_t *lzma = (lzfile_t*)cookie;
348 off_t offset, curpos, newpos;
355 offset = (off_t)*poffset;
356 curpos = (off_t)strm->total_out;
367 if ((offset < 0 && (off_t)(-1 * offset) > curpos) || (offset > 0 && (offset+curpos) < curpos)) {
371 newpos = curpos + offset;
380 if (whence != SEEK_END && newpos < curpos) {
384 lzma->strm = (lzma_stream)LZMA_STREAM_INIT;
388 clearerr(lzma->file);
391 ret = lzmaopen(strm, lzma->encoding ? 'w' : 'r', lzma->what, lzma->level);
392 if (ret != LZMA_OK || strm->total_out != 0) {
399 if (newpos == curpos)
402 char buf[sizeof(lzma->buf)];
404 ssize_t got_size = lzmaread(cookie, buf, sizeof(buf));
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);
427 static int lzmaclose(void *cookie)
429 lzfile_t *lzma = (lzfile_t*)cookie;
430 lzma_ret lret = LZMA_STREAM_END;
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)
445 len = sizeof(lzma->buf) - strm->avail_out;
446 if (len && fwrite(lzma->buf, 1, len, lzma->file) != len)
448 if (lret == LZMA_STREAM_END)
453 fret = fclose(lzma->file);
455 if (lret != LZMA_STREAM_END)
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,
467 #else /* !HAS_LZMA_H */
468 # if defined(HAS_LZMADEC_H)
469 static ssize_t lzmaread(void *cookie, char *buf, size_t count)
475 return lzmadec_read((lzmadec_FILE*)cookie, (uint8_t*)buf, count);
478 static ssize_t lzmawrite(void *cookie, const char *buf, size_t count)
484 static zio_int_t lzmaseek(void *cookie, zio_off_t *poffset, int whence)
490 return (zio_int_t)lzmadec_seek((lzmadec_FILE*)cookie, (off_t)(*poffset), whence);
493 static int lzmaclose(void *cookie)
499 int_fast8_t status = lzmadec_close((lzmadec_FILE*)cookie);
500 return (status >= 0) ? 0 : EOF;
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,
510 # else /* !HAS_LZMADEC_H */
511 # warning No support for .lzma format
512 # endif /* !HAS_LZMADEC_H */
513 #endif /* !HAS_LZMA_H */
515 typedef struct lzwfile_s {
524 static ssize_t lzwread(void *cookie, char *buf, size_t count)
526 lzwfile_t *lzw = (lzwfile_t*)cookie;
531 len = readlzw(lzw->file, buf, count);
533 lzw->position += len;
540 static ssize_t lzwwrite(void *cookie, const char *buf, size_t count)
546 static zio_int_t lzwseek(void *cookie, zio_off_t *poffset, int whence)
548 lzwfile_t *lzw = (lzwfile_t*)cookie;
549 off_t offset, curpos, newpos;
555 offset = (off_t)*poffset;
556 curpos = (off_t)lzw->position;
567 if ((offset < 0 && (off_t)(-1 * offset) > curpos) || (offset > 0 && (offset+curpos) < curpos)) {
571 newpos = curpos + offset;
581 if (whence != SEEK_END && newpos < curpos) {
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);
592 if (lzw->file == (LZW_t*)0) {
597 lzw->position = curpos;
599 if (newpos == curpos)
604 ssize_t got_size = readlzw(lzw->file, buf, sizeof(buf));
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);
624 lzw->position = curpos;
628 static int lzwclose(void *cookie)
630 lzwfile_t *lzw = (lzwfile_t*)cookie;
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,
649 static inline char autodetect(char **__restrict path, const char *__restrict check)
651 const size_t len = strlen(*path);
652 char *suff = strrchr(*path, '.');
658 if (strcmp(suff, "z" ) == 0)
660 else if (strcmp(suff, "gz" ) == 0)
662 else if (strcmp(suff, "Z" ) == 0)
664 else if (strcmp(suff, "bz2" ) == 0)
666 else if (strcmp(suff, "lzma") == 0)
668 else if (strcmp(suff, "xz" ) == 0)
672 if (what == 'n' && *check == 'r') {
676 ext = malloc(sizeof(char)*(len + 5 + 1));
683 if (stat(strcat(ext, ".gz"), &st) == 0) {
688 if (stat(strcat(ext, ".bz2"), &st) == 0) {
693 if (stat(strcat(ext, ".z"), &st) == 0) {
698 if (stat(strcat(ext, ".Z"), &st) == 0) {
703 if (stat(strcat(ext, ".lzma"), &st) == 0) {
708 if (stat(strcat(ext, ".xz"), &st) == 0) {
714 if ((fd = open(ext, O_RDONLY|O_NOCTTY)) < 0)
716 if (read(fd, m, sizeof(m)) == sizeof(m)) {
717 if (m[0] == '\037' && m[1] == '\213')
719 if (m[0] == '\037' && m[1] == '\235')
721 if (m[0] == '\037' && m[1] == '\236')
723 else if (m[0] == 'B' && m[1] == 'Z' && m[2] == 'h')
725 else if (m[0] == ']' && m[1] == '\0' && m[2] == '\0' && m[3] == '\200') /* weak!! */
727 else if (m[0] == '\377' && m[1] == 'L' && m[2] == 'Z' && m[3] == 'M' && m[4] == 'A')
729 else if (m[0] == '\375' && m[1] == '7' && m[2] == 'z' && m[3] == 'X' && m[4] == 'Z')
741 FILE * fzopen(const char * path, const char * mode)
743 FILE * ret = (FILE *)0;
744 char * check = (char*)0, * ext = (char*)0;
749 if (!mode || !(n = strlen(mode))) {
754 if (!(check = (char*)malloc(n*sizeof(char))))
757 /* No append mode possible */
759 case 'r': check[0] = 'r'; break;
760 case 'w': check[0] = 'w'; break;
761 default: errno = EINVAL; goto out;
764 for (i = 1; i < n; i++) {
765 /* We can only open for reading OR writing but NOT for both */
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;
777 if (!path || !(len = strlen(path))) {
783 what = autodetect(&ext, check);
787 case 'z': /* Is this correct? Old gzip magic */
788 #if defined(HAS_ZLIB_H)
792 if (&gzopen == NULL) {
797 if (!(cookie = gzopen(ext, mode))) {
803 if (!(ret = fopencookie((void*)cookie, check, ioz))) {
808 # ifndef LIBIO_IS_FIXED
809 if (ret->_fileno < 0)
813 #else /* !HAS_ZLIB_H */
815 #endif /* !HAS_ZLIB_H */
819 lzwfile_t *__restrict cookie;
825 if (posix_memalign((void*)&cookie, sizeof(void*), alignof(lzwfile_t)+strsize(ext)+strsize(mode)) != 0)
827 memset(cookie, 0, alignof(lzwfile_t)+strsize(ext)+strsize(mode));
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);
835 if (!(cookie->file = openlzw(ext, mode))) {
842 if (!(ret = fopencookie((void*)cookie, check, iolzw))) {
843 closelzw(cookie->file);
848 # ifndef LIBIO_IS_FIXED
849 if (ret->_fileno < 0)
856 #if defined(HAS_BZLIB_H)
858 bzfile_t *__restrict cookie;
861 if (&BZ2_bzopen == NULL) {
866 if (posix_memalign((void*)&cookie, sizeof(void*), alignof(bzfile_t)+strsize(ext)+strsize(mode)) != 0)
868 memset(cookie, 0, alignof(bzfile_t)+strsize(ext)+strsize(mode));
870 for (i = 1; i < n; i++) {
871 if (mode[i] >= '0' && mode[i] <= '9') {
872 level = (int)mode[i];
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);
883 if (!(cookie->file = BZ2_bzopen(ext, mode))) {
890 if (!(ret = fopencookie((void*)cookie, check, iobz))) {
891 BZ2_bzclose(cookie->file);
896 # ifndef LIBIO_IS_FIXED
897 if (ret->_fileno < 0)
902 #else /* !HAS_BZLIB_H */
904 #endif /* !HAS_BZLIB_H */
906 #if defined(HAS_LZMA_H)
910 int level = LZMA_PRESET_DEFAULT;
911 lzfile_t *__restrict cookie;
914 if (&lzma_auto_decoder == NULL) {
919 if (posix_memalign((void*)&cookie, sizeof(void*), alignof(lzfile_t)) != 0)
921 memset(cookie, 0, alignof(lzfile_t));
923 if ((cookie->file = fopen(ext, check)) == NULL) {
928 for (i = 1; i < n; i++) {
929 if (mode[i] >= '0' && mode[i] <= '9') {
930 level = (int)mode[i];
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);
942 if (lret != LZMA_OK) {
943 fclose(cookie->file);
948 if (!(ret = fopencookie((void*)cookie, check, iolzma))) {
949 lzma_end(&cookie->strm);
950 fclose(cookie->file);
955 # ifndef LIBIO_IS_FIXED
956 if (ret->_fileno < 0)
962 #else /* !HAS_LZMA_H */
967 # if defined(HAS_LZMADEC_H)
969 lzmadec_FILE* cookie;
976 if (&lzmadec_open == NULL) {
981 if (!(cookie = lzmadec_open(ext))) {
987 if (!(ret = fopencookie((void*)cookie, check, iolzma))) {
988 lzmadec_close(cookie);
992 # ifndef LIBIO_IS_FIXED
993 if (ret->_fileno < 0)
998 # else /* !HAS_LZMADEC_H */
1000 # endif /* !HAS_LZMADEC_H */
1001 #endif /* !HAS_LZMA_H */
1004 ret = fopen(ext, mode);
1011 if (ext && ext != path)
1017 FILE * fdzopen(int fildes, const char * mode, const char *what)
1019 FILE * ret = (FILE *)0;
1020 char * check = (char*)0;
1024 if (!mode || !(n = strlen(mode))) {
1029 if (!(check = (char*)malloc(n*sizeof(char))))
1032 /* No append mode possible */
1034 case 'r': check[0] = 'r'; break;
1035 case 'w': check[0] = 'w'; break;
1036 default: errno = EINVAL; goto out;
1039 for (i = 1; i < n; i++) {
1040 /* We can only open for reading OR writing but NOT for both */
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;
1054 case 'z': /* Is this correct? Old gzip magic */
1055 #if defined(HAS_ZLIB_H)
1059 if (&gzdopen == NULL) {
1064 if (!(cookie = gzdopen(fildes, mode))) {
1070 if (!(ret = fopencookie((void*)cookie, check, ioz))) {
1075 # ifndef LIBIO_IS_FIXED
1076 if (ret->_fileno < 0)
1080 #else /* !HAS_ZLIB_H */
1082 #endif /* !HAS_ZLIB_H */
1086 lzwfile_t *__restrict cookie;
1092 if (posix_memalign((void*)&cookie, sizeof(void*), alignof(lzwfile_t)+strsize(mode)) != 0)
1094 memset(cookie, 0, alignof(lzwfile_t)+strsize(mode));
1096 cookie->fd = fildes;
1097 cookie->mode = ((char*)cookie)+alignof(lzwfile_t);
1098 strcpy(cookie->mode, mode);
1100 if (!(cookie->file = dopenlzw(fildes, mode))) {
1107 if (!(ret = fopencookie((void*)cookie, check, iolzw))) {
1108 closelzw(cookie->file);
1113 # ifndef LIBIO_IS_FIXED
1114 if (ret->_fileno < 0)
1117 cookie->stdio = ret;
1121 #if defined(HAS_BZLIB_H)
1123 bzfile_t *__restrict cookie;
1126 if (&BZ2_bzdopen == NULL) {
1131 if (posix_memalign((void*)&cookie, sizeof(void*), alignof(bzfile_t)+strsize(mode)) != 0)
1133 memset(cookie, 0, alignof(bzfile_t)+strsize(mode));
1135 for (i = 1; i < n; i++) {
1136 if (mode[i] >= '0' && mode[i] <= '9') {
1137 level = (int)mode[i];
1142 cookie->fd = fildes;
1143 cookie->mode = ((char*)cookie)+alignof(bzfile_t);
1144 strcpy(cookie->mode, mode);
1146 if (cookie->mode == (char*)0) {
1150 if (!(cookie->file = BZ2_bzdopen(fildes, mode))) {
1157 if (!(ret = fopencookie((void*)cookie, check, iobz))) {
1158 BZ2_bzclose(cookie->file);
1163 # ifndef LIBIO_IS_FIXED
1164 if (ret->_fileno < 0)
1167 cookie->stdio = ret;
1169 #else /* !HAS_BZLIB_H */
1171 #endif /* !HAS_BZLIB_H */
1173 #if defined(HAS_LZMA_H)
1177 int level = LZMA_PRESET_DEFAULT;
1178 lzfile_t *__restrict cookie;
1181 if (&lzma_auto_decoder == NULL) {
1186 if (posix_memalign((void*)&cookie, sizeof(void*), alignof(lzfile_t)) != 0)
1188 memset(cookie, 0, alignof(lzfile_t));
1190 if ((cookie->file = fdopen(fildes, check)) == NULL) {
1195 for (i = 1; i < n; i++) {
1196 if (mode[i] >= '0' && mode[i] <= '9') {
1197 level = (int)mode[i];
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);
1209 if (lret != LZMA_OK) {
1210 fclose(cookie->file);
1215 if (!(ret = fopencookie((void*)cookie, check, iolzma))) {
1216 lzma_end(&cookie->strm);
1217 fclose(cookie->file);
1222 # ifndef LIBIO_IS_FIXED
1223 if (ret->_fileno < 0)
1226 cookie->stdio = ret;
1229 #else /* !HAS_LZMA_H */
1234 # if defined(HAS_LZMADEC_H)
1236 lzmadec_FILE* cookie;
1243 if (&lzmadec_open == NULL) {
1248 if (!(cookie = lzmadec_dopen(fildes))) {
1254 if (!(ret = fopencookie((void*)cookie, check, iolzma))) {
1255 lzmadec_close(cookie);
1259 # ifndef LIBIO_IS_FIXED
1260 if (ret->_fileno < 0)
1263 cookie->stdio = ret;
1265 # else /* !HAS_LZMADEC_H */
1267 # endif /* !HAS_LZMADEC_H */
1268 #endif /* !HAS_LZMA_H */
1271 ret = fdopen(fildes, mode);