1 #pragma prototyped
2 
3 /*
4  * gzip encoder/decoder
5  */
6 
7 #include <codex.h>
8 #include <zlib.h>
9 #include <ctype.h>
10 
11 #define MINLEVEL	1
12 #define MAXLEVEL	9
13 
14 #define X(x)		#x
15 #define S(x)		X(x)
16 
17 typedef struct State_s
18 {
19 	Codex_t*	codex;
20 	gzFile*		gz;
21 	int		crc;
22 	int		level;
23 } State_t;
24 
25 static int
gzip_ident(Codexmeth_t * meth,const void * head,size_t headsize,char * name,size_t namesize)26 gzip_ident(Codexmeth_t* meth, const void* head, size_t headsize, char* name, size_t namesize)
27 {
28 	unsigned char*	h = (unsigned char*)head;
29 
30 	if (headsize >= 3 && h[0] == 0x1f && h[1] == 0x8b)
31 	{
32 		strncopy(name, meth->name, namesize);
33 		return 1;
34 	}
35 	return 0;
36 }
37 
38 static int
gzip_open(Codex_t * p,char * const args[],Codexnum_t flags)39 gzip_open(Codex_t* p, char* const args[], Codexnum_t flags)
40 {
41 	register State_t*	state;
42 	const char*		s;
43 	char*			e;
44 	int			i;
45 	int			v;
46 	int			crc = 1;
47 	int			level = MAXLEVEL;
48 
49 	i = 2;
50 	while (s = args[i++])
51 	{
52 		if (isdigit(*s))
53 			v = strton(s, &e, NiL, 0);
54 		else
55 		{
56 			e = (char*)s;
57 			if (e[0] == 'n' && e[1] == 'o')
58 			{
59 				e += 2;
60 				v = 0;
61 			}
62 			else
63 				v = 1;
64 		}
65 		if (!*e)
66 		{
67 			if (v < MINLEVEL || v > MAXLEVEL)
68 			{
69 				if (p->disc->errorf)
70 					(*p->disc->errorf)(NiL, p->disc, 2, "%s: compression level must be in [%d..%d]", s, MINLEVEL, MAXLEVEL);
71 				return -1;
72 			}
73 			level = v;
74 		}
75 		else if (!streq(e, "crc"))
76 			crc = v;
77 		else
78 		{
79 			if (p->disc->errorf)
80 				(*p->disc->errorf)(NiL, p->disc, 2, "%s: unknown option", s);
81 			return -1;
82 		}
83 	}
84 	if (!(p->op = sfopen(NiL, "/dev/null", (p->flags & CODEX_DECODE) ? "r" : "w")))
85 	{
86 		if (p->disc->errorf)
87 			(*p->disc->errorf)(NiL, p->disc, 2, "cannot swap main stream");
88 		return -1;
89 	}
90 	sfswap(p->op, p->sp);
91 	if (!(state = newof(0, State_t, 1, 0)))
92 	{
93 		if (p->disc->errorf)
94 			(*p->disc->errorf)(NiL, p->disc, 2, "out of space");
95 		return -1;
96 	}
97 	state->codex = p;
98 	state->crc = crc;
99 	state->level = level;
100 	p->data = state;
101 	return 0;
102 }
103 
104 static int
gzip_init(Codex_t * p)105 gzip_init(Codex_t* p)
106 {
107 	State_t*	state = (State_t*)p->data;
108 	char*		m;
109 	char		mode[8];
110 
111 	m = mode;
112 	if (p->flags & CODEX_ENCODE)
113 		*m++ = 'w';
114 	else
115 		*m++ = 'r';
116 	*m++ = 'b';
117 	if (!state->crc)
118 		*m++ = 'c';
119 	*m++ = 'o';
120 	*m++ = '0' + state->level;
121 	*m = 0;
122 	return (state->gz = gzfopen(p->op, mode)) ? 0 : -1;
123 }
124 
125 static int
gzip_done(Codex_t * p)126 gzip_done(Codex_t* p)
127 {
128 	State_t*	state = (State_t*)p->data;
129 	int		r;
130 
131 	r = gzclose(state->gz) ? -1 : 0;
132 	free(state);
133 	return r;
134 }
135 
136 static ssize_t
gzip_read(Sfio_t * f,Void_t * buf,size_t n,Sfdisc_t * dp)137 gzip_read(Sfio_t* f, Void_t* buf, size_t n, Sfdisc_t* dp)
138 {
139 	return gzread(((State_t*)CODEX(dp)->data)->gz, buf, n);
140 }
141 
142 static ssize_t
gzip_write(Sfio_t * f,const Void_t * buf,size_t n,Sfdisc_t * dp)143 gzip_write(Sfio_t* f, const Void_t* buf, size_t n, Sfdisc_t* dp)
144 {
145 	return gzwrite(((State_t*)CODEX(dp)->data)->gz, buf, n);
146 }
147 
148 Codexmeth_t	codex_gzip =
149 {
150 	"gzip",
151 	"gzip compression. The first parameter is the compression level,"
152 	" " S(MINLEVEL) ":least, " S(MAXLEVEL) ":most(default). nocrc disables crc checks.",
153 	0,
154 	CODEX_DECODE|CODEX_ENCODE|CODEX_COMPRESS,
155 	0,
156 	gzip_ident,
157 	gzip_open,
158 	0,
159 	gzip_init,
160 	gzip_done,
161 	gzip_read,
162 	gzip_write,
163 	0,
164 	0,
165 	0,
166 	0,
167 	0,
168 	CODEXNEXT(gzip)
169 };
170 
171 CODEXLIB(gzip)
172