xref: /freebsd/stand/libsa/bzipfs.c (revision 3e15b01d)
1ca987d46SWarner Losh /*
2ca987d46SWarner Losh  * Copyright (c) 1998 Michael Smith.
3ca987d46SWarner Losh  * Copyright (c) 2000 Maxim Sobolev
4ca987d46SWarner Losh  * All rights reserved.
5ca987d46SWarner Losh  *
6ca987d46SWarner Losh  * Redistribution and use in source and binary forms, with or without
7ca987d46SWarner Losh  * modification, are permitted provided that the following conditions
8ca987d46SWarner Losh  * are met:
9ca987d46SWarner Losh  * 1. Redistributions of source code must retain the above copyright
10ca987d46SWarner Losh  *    notice, this list of conditions and the following disclaimer.
11ca987d46SWarner Losh  * 2. Redistributions in binary form must reproduce the above copyright
12ca987d46SWarner Losh  *    notice, this list of conditions and the following disclaimer in the
13ca987d46SWarner Losh  *    documentation and/or other materials provided with the distribution.
14ca987d46SWarner Losh  *
15ca987d46SWarner Losh  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16ca987d46SWarner Losh  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17ca987d46SWarner Losh  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18ca987d46SWarner Losh  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19ca987d46SWarner Losh  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20ca987d46SWarner Losh  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21ca987d46SWarner Losh  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22ca987d46SWarner Losh  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23ca987d46SWarner Losh  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24ca987d46SWarner Losh  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25ca987d46SWarner Losh  * SUCH DAMAGE.
26ca987d46SWarner Losh  */
27ca987d46SWarner Losh 
28ca987d46SWarner Losh #ifndef REGRESSION
29ca987d46SWarner Losh #include "stand.h"
30ca987d46SWarner Losh #else
31ca987d46SWarner Losh #include <stdlib.h>
32ca987d46SWarner Losh #include <sys/errno.h>
33ca987d46SWarner Losh #include <sys/fcntl.h>
34ca987d46SWarner Losh #include <sys/types.h>
35ca987d46SWarner Losh #include <sys/unistd.h>
36ca987d46SWarner Losh 
37ca987d46SWarner Losh struct open_file {
38ca987d46SWarner Losh     int                 f_flags;        /* see F_* below */
39ca987d46SWarner Losh     void                *f_fsdata;      /* file system specific data */
40ca987d46SWarner Losh };
41ca987d46SWarner Losh #define F_READ          0x0001  /* file opened for reading */
42ca987d46SWarner Losh #define EOFFSET (ELAST+8)       /* relative seek not supported */
min(u_int a,u_int b)43ca987d46SWarner Losh static inline u_int min(u_int a, u_int b) { return(a < b ? a : b); }
44ca987d46SWarner Losh #define panic(x, y) abort()
45ca987d46SWarner Losh #endif
46ca987d46SWarner Losh 
47ca987d46SWarner Losh #include <sys/stat.h>
48ca987d46SWarner Losh #include <string.h>
49ca987d46SWarner Losh #include <bzlib.h>
50ca987d46SWarner Losh 
51ca987d46SWarner Losh #define BZ_BUFSIZE 2048	/* XXX larger? */
52ca987d46SWarner Losh 
53ca987d46SWarner Losh struct bz_file
54ca987d46SWarner Losh {
55ca987d46SWarner Losh     int			bzf_rawfd;
56ca987d46SWarner Losh     bz_stream		bzf_bzstream;
57ca987d46SWarner Losh     char		bzf_buf[BZ_BUFSIZE];
58ca987d46SWarner Losh     int			bzf_endseen;
59ca987d46SWarner Losh };
60ca987d46SWarner Losh 
61ca987d46SWarner Losh static int	bzf_fill(struct bz_file *z);
62ca987d46SWarner Losh static int	bzf_open(const char *path, struct open_file *f);
63ca987d46SWarner Losh static int	bzf_close(struct open_file *f);
64ca987d46SWarner Losh static int	bzf_read(struct open_file *f, void *buf, size_t size, size_t *resid);
65ca987d46SWarner Losh static off_t	bzf_seek(struct open_file *f, off_t offset, int where);
66ca987d46SWarner Losh static int	bzf_stat(struct open_file *f, struct stat *sb);
67ca987d46SWarner Losh 
68ca987d46SWarner Losh #ifndef REGRESSION
69ca987d46SWarner Losh struct fs_ops bzipfs_fsops = {
700ad8a113SWarner Losh 	.fs_name = "bzip",
710ad8a113SWarner Losh 	.fo_open = bzf_open,
720ad8a113SWarner Losh 	.fo_close = bzf_close,
730ad8a113SWarner Losh 	.fo_read = bzf_read,
740ad8a113SWarner Losh 	.fo_write = null_write,
750ad8a113SWarner Losh 	.fo_seek = bzf_seek,
760ad8a113SWarner Losh 	.fo_stat = bzf_stat,
770ad8a113SWarner Losh 	.fo_readdir = null_readdir,
78ca987d46SWarner Losh };
79ca987d46SWarner Losh #endif
80ca987d46SWarner Losh 
81ca987d46SWarner Losh static int
bzf_fill(struct bz_file * bzf)82ca987d46SWarner Losh bzf_fill(struct bz_file *bzf)
83ca987d46SWarner Losh {
84ca987d46SWarner Losh     int		result;
85ca987d46SWarner Losh     int		req;
86ca987d46SWarner Losh 
87ca987d46SWarner Losh     req = BZ_BUFSIZE - bzf->bzf_bzstream.avail_in;
88ca987d46SWarner Losh     result = 0;
89ca987d46SWarner Losh 
90ca987d46SWarner Losh     /* If we need more */
91ca987d46SWarner Losh     if (req > 0) {
92ca987d46SWarner Losh 	/* move old data to bottom of buffer */
93ca987d46SWarner Losh 	if (req < BZ_BUFSIZE)
94ca987d46SWarner Losh 	    bcopy(bzf->bzf_buf + req, bzf->bzf_buf, BZ_BUFSIZE - req);
95ca987d46SWarner Losh 
96ca987d46SWarner Losh 	/* read to fill buffer and update availibility data */
97ca987d46SWarner Losh 	result = read(bzf->bzf_rawfd, bzf->bzf_buf + bzf->bzf_bzstream.avail_in, req);
98ca987d46SWarner Losh 	bzf->bzf_bzstream.next_in = bzf->bzf_buf;
99ca987d46SWarner Losh 	if (result >= 0)
100ca987d46SWarner Losh 	    bzf->bzf_bzstream.avail_in += result;
101ca987d46SWarner Losh     }
102ca987d46SWarner Losh     return(result);
103ca987d46SWarner Losh }
104ca987d46SWarner Losh 
105ca987d46SWarner Losh /*
106ca987d46SWarner Losh  * Adapted from get_byte/check_header in libz
107ca987d46SWarner Losh  *
108ca987d46SWarner Losh  * Returns 0 if the header is OK, nonzero if not.
109ca987d46SWarner Losh  */
110ca987d46SWarner Losh static int
get_byte(struct bz_file * bzf)111ca987d46SWarner Losh get_byte(struct bz_file *bzf)
112ca987d46SWarner Losh {
113ca987d46SWarner Losh     if ((bzf->bzf_bzstream.avail_in == 0) && (bzf_fill(bzf) == -1))
114ca987d46SWarner Losh 	return(-1);
115ca987d46SWarner Losh     bzf->bzf_bzstream.avail_in--;
116ca987d46SWarner Losh     return(*(bzf->bzf_bzstream.next_in)++);
117ca987d46SWarner Losh }
118ca987d46SWarner Losh 
119ca987d46SWarner Losh static int bz_magic[3] = {'B', 'Z', 'h'}; /* bzip2 magic header */
120ca987d46SWarner Losh 
121ca987d46SWarner Losh static int
check_header(struct bz_file * bzf)122ca987d46SWarner Losh check_header(struct bz_file *bzf)
123ca987d46SWarner Losh {
124ca987d46SWarner Losh     unsigned int len;
125ca987d46SWarner Losh     int		 c;
126ca987d46SWarner Losh 
127ca987d46SWarner Losh     /* Check the bzip2 magic header */
128ca987d46SWarner Losh     for (len = 0; len < 3; len++) {
129ca987d46SWarner Losh 	c = get_byte(bzf);
130ca987d46SWarner Losh 	if (c != bz_magic[len]) {
131ca987d46SWarner Losh 	    return(1);
132ca987d46SWarner Losh 	}
133ca987d46SWarner Losh     }
134ca987d46SWarner Losh     /* Check that the block size is valid */
135ca987d46SWarner Losh     c = get_byte(bzf);
136ca987d46SWarner Losh     if (c < '1' || c > '9')
137ca987d46SWarner Losh 	return(1);
138ca987d46SWarner Losh 
139ca987d46SWarner Losh     /* Put back bytes that we've took from the input stream */
140ca987d46SWarner Losh     bzf->bzf_bzstream.next_in -= 4;
141ca987d46SWarner Losh     bzf->bzf_bzstream.avail_in += 4;
142ca987d46SWarner Losh 
143ca987d46SWarner Losh     return(0);
144ca987d46SWarner Losh }
145ca987d46SWarner Losh 
146ca987d46SWarner Losh static int
bzf_open(const char * fname,struct open_file * f)147ca987d46SWarner Losh bzf_open(const char *fname, struct open_file *f)
148ca987d46SWarner Losh {
149ca987d46SWarner Losh     static char		*bzfname;
150ca987d46SWarner Losh     int			rawfd;
151ca987d46SWarner Losh     struct bz_file	*bzf;
152ca987d46SWarner Losh     char		*cp;
153ca987d46SWarner Losh     int			error;
154ca987d46SWarner Losh     struct stat		sb;
155ca987d46SWarner Losh 
156ca987d46SWarner Losh     /* Have to be in "just read it" mode */
157ca987d46SWarner Losh     if (f->f_flags != F_READ)
158ca987d46SWarner Losh 	return(EPERM);
159ca987d46SWarner Losh 
160ca987d46SWarner Losh     /* If the name already ends in .gz or .bz2, ignore it */
161ca987d46SWarner Losh     if ((cp = strrchr(fname, '.')) && (!strcmp(cp, ".gz")
162ca987d46SWarner Losh 	    || !strcmp(cp, ".bz2") || !strcmp(cp, ".split")))
163ca987d46SWarner Losh 	return(ENOENT);
164ca987d46SWarner Losh 
165ca987d46SWarner Losh     /* Construct new name */
166ca987d46SWarner Losh     bzfname = malloc(strlen(fname) + 5);
167ca987d46SWarner Losh     if (bzfname == NULL)
168ca987d46SWarner Losh 	return(ENOMEM);
169ca987d46SWarner Losh     sprintf(bzfname, "%s.bz2", fname);
170ca987d46SWarner Losh 
171ca987d46SWarner Losh     /* Try to open the compressed datafile */
172ca987d46SWarner Losh     rawfd = open(bzfname, O_RDONLY);
173ca987d46SWarner Losh     free(bzfname);
174ca987d46SWarner Losh     if (rawfd == -1)
175ca987d46SWarner Losh 	return(ENOENT);
176ca987d46SWarner Losh 
177ca987d46SWarner Losh     if (fstat(rawfd, &sb) < 0) {
178ca987d46SWarner Losh 	printf("bzf_open: stat failed\n");
179ca987d46SWarner Losh 	close(rawfd);
180ca987d46SWarner Losh 	return(ENOENT);
181ca987d46SWarner Losh     }
182ca987d46SWarner Losh     if (!S_ISREG(sb.st_mode)) {
183ca987d46SWarner Losh 	printf("bzf_open: not a file\n");
184ca987d46SWarner Losh 	close(rawfd);
185ca987d46SWarner Losh 	return(EISDIR);			/* best guess */
186ca987d46SWarner Losh     }
187ca987d46SWarner Losh 
188ca987d46SWarner Losh     /* Allocate a bz_file structure, populate it */
189ca987d46SWarner Losh     bzf = malloc(sizeof(struct bz_file));
190ca987d46SWarner Losh     if (bzf == NULL)
191ca987d46SWarner Losh 	return(ENOMEM);
192ca987d46SWarner Losh     bzero(bzf, sizeof(struct bz_file));
193ca987d46SWarner Losh     bzf->bzf_rawfd = rawfd;
194ca987d46SWarner Losh 
195ca987d46SWarner Losh     /* Verify that the file is bzipped */
196ca987d46SWarner Losh     if (check_header(bzf)) {
197ca987d46SWarner Losh 	close(bzf->bzf_rawfd);
198ca987d46SWarner Losh 	free(bzf);
199ca987d46SWarner Losh 	return(EFTYPE);
200ca987d46SWarner Losh     }
201ca987d46SWarner Losh 
202ca987d46SWarner Losh     /* Initialise the inflation engine */
203ca987d46SWarner Losh     if ((error = BZ2_bzDecompressInit(&(bzf->bzf_bzstream), 0, 1)) != BZ_OK) {
204ca987d46SWarner Losh 	printf("bzf_open: BZ2_bzDecompressInit returned %d\n", error);
205ca987d46SWarner Losh 	close(bzf->bzf_rawfd);
206ca987d46SWarner Losh 	free(bzf);
207ca987d46SWarner Losh 	return(EIO);
208ca987d46SWarner Losh     }
209ca987d46SWarner Losh 
210ca987d46SWarner Losh     /* Looks OK, we'll take it */
211ca987d46SWarner Losh     f->f_fsdata = bzf;
212ca987d46SWarner Losh     return(0);
213ca987d46SWarner Losh }
214ca987d46SWarner Losh 
215ca987d46SWarner Losh static int
bzf_close(struct open_file * f)216ca987d46SWarner Losh bzf_close(struct open_file *f)
217ca987d46SWarner Losh {
218ca987d46SWarner Losh     struct bz_file	*bzf = (struct bz_file *)f->f_fsdata;
219ca987d46SWarner Losh 
220ca987d46SWarner Losh     BZ2_bzDecompressEnd(&(bzf->bzf_bzstream));
221ca987d46SWarner Losh     close(bzf->bzf_rawfd);
222ca987d46SWarner Losh     free(bzf);
223ca987d46SWarner Losh     return(0);
224ca987d46SWarner Losh }
225ca987d46SWarner Losh 
226ca987d46SWarner Losh static int
bzf_read(struct open_file * f,void * buf,size_t size,size_t * resid)227ca987d46SWarner Losh bzf_read(struct open_file *f, void *buf, size_t size, size_t *resid)
228ca987d46SWarner Losh {
229ca987d46SWarner Losh     struct bz_file	*bzf = (struct bz_file *)f->f_fsdata;
230ca987d46SWarner Losh     int			error;
231ca987d46SWarner Losh 
232ca987d46SWarner Losh     bzf->bzf_bzstream.next_out = buf;			/* where and how much */
233ca987d46SWarner Losh     bzf->bzf_bzstream.avail_out = size;
234ca987d46SWarner Losh 
235ca987d46SWarner Losh     while (bzf->bzf_bzstream.avail_out && bzf->bzf_endseen == 0) {
236ca987d46SWarner Losh 	if ((bzf->bzf_bzstream.avail_in == 0) && (bzf_fill(bzf) == -1)) {
237ca987d46SWarner Losh 	    printf("bzf_read: fill error\n");
238ca987d46SWarner Losh 	    return(EIO);
239ca987d46SWarner Losh 	}
240ca987d46SWarner Losh 	if (bzf->bzf_bzstream.avail_in == 0) {		/* oops, unexpected EOF */
241ca987d46SWarner Losh 	    printf("bzf_read: unexpected EOF\n");
242ca987d46SWarner Losh 	    if (bzf->bzf_bzstream.avail_out == size)
243ca987d46SWarner Losh 		return(EIO);
244ca987d46SWarner Losh 	    break;
245ca987d46SWarner Losh 	}
246ca987d46SWarner Losh 
247ca987d46SWarner Losh 	error = BZ2_bzDecompress(&bzf->bzf_bzstream);	/* decompression pass */
248ca987d46SWarner Losh 	if (error == BZ_STREAM_END) {			/* EOF, all done */
249ca987d46SWarner Losh 	    bzf->bzf_endseen = 1;
250ca987d46SWarner Losh 	    break;
251ca987d46SWarner Losh 	}
252ca987d46SWarner Losh 	if (error != BZ_OK) {				/* argh, decompression error */
253ca987d46SWarner Losh 	    printf("bzf_read: BZ2_bzDecompress returned %d\n", error);
254ca987d46SWarner Losh 	    return(EIO);
255ca987d46SWarner Losh 	}
256ca987d46SWarner Losh     }
257ca987d46SWarner Losh     if (resid != NULL)
258ca987d46SWarner Losh 	*resid = bzf->bzf_bzstream.avail_out;
259ca987d46SWarner Losh     return(0);
260ca987d46SWarner Losh }
261ca987d46SWarner Losh 
262ca987d46SWarner Losh static int
bzf_rewind(struct open_file * f)263ca987d46SWarner Losh bzf_rewind(struct open_file *f)
264ca987d46SWarner Losh {
265ca987d46SWarner Losh     struct bz_file	*bzf = (struct bz_file *)f->f_fsdata;
266ca987d46SWarner Losh     struct bz_file	*bzf_tmp;
267ca987d46SWarner Losh 
268ca987d46SWarner Losh     /*
269ca987d46SWarner Losh      * Since bzip2 does not have an equivalent inflateReset function a crude
270ca987d46SWarner Losh      * one needs to be provided.  The functions all called in such a way that
271ca987d46SWarner Losh      * at any time an error occurs a roll back can be done (effectively making
272ca987d46SWarner Losh      * this rewind 'atomic', either the reset occurs successfully or not at all,
273ca987d46SWarner Losh      * with no 'undefined' state happening).
274ca987d46SWarner Losh      */
275ca987d46SWarner Losh 
276ca987d46SWarner Losh     /* Allocate a bz_file structure, populate it */
277ca987d46SWarner Losh     bzf_tmp = malloc(sizeof(struct bz_file));
278ca987d46SWarner Losh     if (bzf_tmp == NULL)
279ca987d46SWarner Losh 	return(-1);
280ca987d46SWarner Losh     bzero(bzf_tmp, sizeof(struct bz_file));
281ca987d46SWarner Losh     bzf_tmp->bzf_rawfd = bzf->bzf_rawfd;
282ca987d46SWarner Losh 
283ca987d46SWarner Losh     /* Initialise the inflation engine */
284ca987d46SWarner Losh     if (BZ2_bzDecompressInit(&(bzf_tmp->bzf_bzstream), 0, 1) != BZ_OK) {
285ca987d46SWarner Losh 	free(bzf_tmp);
286ca987d46SWarner Losh 	return(-1);
287ca987d46SWarner Losh     }
288ca987d46SWarner Losh 
289ca987d46SWarner Losh     /* Seek back to the beginning of the file */
290ca987d46SWarner Losh     if (lseek(bzf->bzf_rawfd, 0, SEEK_SET) == -1) {
291ca987d46SWarner Losh 	BZ2_bzDecompressEnd(&(bzf_tmp->bzf_bzstream));
292ca987d46SWarner Losh 	free(bzf_tmp);
293ca987d46SWarner Losh 	return(-1);
294ca987d46SWarner Losh     }
295ca987d46SWarner Losh 
296ca987d46SWarner Losh     /* Free old bz_file data */
297ca987d46SWarner Losh     BZ2_bzDecompressEnd(&(bzf->bzf_bzstream));
298ca987d46SWarner Losh     free(bzf);
299ca987d46SWarner Losh 
300ca987d46SWarner Losh     /* Use the new bz_file data */
301ca987d46SWarner Losh     f->f_fsdata = bzf_tmp;
302ca987d46SWarner Losh 
303ca987d46SWarner Losh     return(0);
304ca987d46SWarner Losh }
305ca987d46SWarner Losh 
306ca987d46SWarner Losh static off_t
bzf_seek(struct open_file * f,off_t offset,int where)307ca987d46SWarner Losh bzf_seek(struct open_file *f, off_t offset, int where)
308ca987d46SWarner Losh {
309ca987d46SWarner Losh     struct bz_file	*bzf = (struct bz_file *)f->f_fsdata;
310ca987d46SWarner Losh     off_t		target;
311ca987d46SWarner Losh     char		discard[16];
312ca987d46SWarner Losh 
313ca987d46SWarner Losh     switch (where) {
314ca987d46SWarner Losh     case SEEK_SET:
315ca987d46SWarner Losh 	target = offset;
316ca987d46SWarner Losh 	break;
317ca987d46SWarner Losh     case SEEK_CUR:
318ca987d46SWarner Losh 	target = offset + bzf->bzf_bzstream.total_out_lo32;
319ca987d46SWarner Losh 	break;
320ca987d46SWarner Losh     default:
321ca987d46SWarner Losh 	errno = EINVAL;
322ca987d46SWarner Losh 	return(-1);
323ca987d46SWarner Losh     }
324ca987d46SWarner Losh 
325ca987d46SWarner Losh     /* Can we get there from here? */
326ca987d46SWarner Losh     if (target < bzf->bzf_bzstream.total_out_lo32 && bzf_rewind(f) != 0) {
327ca987d46SWarner Losh 	errno = EOFFSET;
328ca987d46SWarner Losh 	return -1;
329ca987d46SWarner Losh     }
330ca987d46SWarner Losh 
331ca987d46SWarner Losh     /* if bzf_rewind was called then bzf has changed */
332ca987d46SWarner Losh     bzf = (struct bz_file *)f->f_fsdata;
333ca987d46SWarner Losh 
334ca987d46SWarner Losh     /* skip forwards if required */
335ca987d46SWarner Losh     while (target > bzf->bzf_bzstream.total_out_lo32) {
336ca987d46SWarner Losh 	errno = bzf_read(f, discard, min(sizeof(discard),
337ca987d46SWarner Losh 	    target - bzf->bzf_bzstream.total_out_lo32), NULL);
338ca987d46SWarner Losh 	if (errno)
339ca987d46SWarner Losh 	    return(-1);
3403df4c387SDavid Bright 	/* Break out of loop if end of file has been reached. */
3413df4c387SDavid Bright 	if (bzf->bzf_endseen)
3423df4c387SDavid Bright 	    break;
343ca987d46SWarner Losh     }
344ca987d46SWarner Losh     /* This is where we are (be honest if we overshot) */
345ca987d46SWarner Losh     return(bzf->bzf_bzstream.total_out_lo32);
346ca987d46SWarner Losh }
347ca987d46SWarner Losh 
348ca987d46SWarner Losh static int
bzf_stat(struct open_file * f,struct stat * sb)349ca987d46SWarner Losh bzf_stat(struct open_file *f, struct stat *sb)
350ca987d46SWarner Losh {
351ca987d46SWarner Losh     struct bz_file	*bzf = (struct bz_file *)f->f_fsdata;
352ca987d46SWarner Losh     int			result;
353ca987d46SWarner Losh 
354ca987d46SWarner Losh     /* stat as normal, but indicate that size is unknown */
355ca987d46SWarner Losh     if ((result = fstat(bzf->bzf_rawfd, sb)) == 0)
356ca987d46SWarner Losh 	sb->st_size = -1;
357ca987d46SWarner Losh     return(result);
358ca987d46SWarner Losh }
359ca987d46SWarner Losh 
360ca987d46SWarner Losh void
bz_internal_error(int errorcode)361ca987d46SWarner Losh bz_internal_error(int errorcode)
362ca987d46SWarner Losh {
363746cc38eSGordon Bergling     panic("bzipfs: critical error %d in bzip2 library occurred", errorcode);
364ca987d46SWarner Losh }
365ca987d46SWarner Losh 
366ca987d46SWarner Losh #ifdef REGRESSION
367ca987d46SWarner Losh /* Small test case, open and decompress test.bz2 */
main(void)368dcc20bceSWarner Losh int main(void)
369ca987d46SWarner Losh {
370ca987d46SWarner Losh     struct open_file f;
371ca987d46SWarner Losh     char buf[1024];
372ca987d46SWarner Losh     size_t resid;
373ca987d46SWarner Losh     int err;
374ca987d46SWarner Losh 
375ca987d46SWarner Losh     memset(&f, '\0', sizeof(f));
376ca987d46SWarner Losh     f.f_flags = F_READ;
377ca987d46SWarner Losh     err = bzf_open("test", &f);
378ca987d46SWarner Losh     if (err != 0)
379ca987d46SWarner Losh 	exit(1);
380ca987d46SWarner Losh     do {
381ca987d46SWarner Losh 	err = bzf_read(&f, buf, sizeof(buf), &resid);
382ca987d46SWarner Losh     } while (err == 0 && resid != sizeof(buf));
383ca987d46SWarner Losh 
384ca987d46SWarner Losh     if (err != 0)
385ca987d46SWarner Losh 	exit(2);
386ca987d46SWarner Losh     exit(0);
387ca987d46SWarner Losh }
388ca987d46SWarner Losh #endif
389