1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *            Copyright (c) 2011 AT&T Intellectual Property             *
5 *                      and is licensed under the                       *
6 *                 Eclipse Public License, Version 1.0                  *
7 *                    by AT&T Intellectual Property                     *
8 *                                                                      *
9 *                A copy of the License is available at                 *
10 *          http://www.eclipse.org/org/documents/epl-v10.html           *
11 *         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
12 *                                                                      *
13 *              Information and Software Systems Research               *
14 *                            AT&T Research                             *
15 *                           Florham Park NJ                            *
16 *                                                                      *
17 *                 Glenn Fowler <gsf@research.att.com>                  *
18 *                                                                      *
19 ***********************************************************************/
20 #pragma prototyped
21 
22 /*
23  * pax ico format
24  */
25 
26 #include <paxlib.h>
27 #include <ccode.h>
28 #include <swap.h>
29 #include <tm.h>
30 
31 #define ICO_HEADER	6
32 #define ICO_DIRECTORY	16
33 
34 #define ICO_MAGIC	0
35 #define ICO_TYPE	1
36 
37 #define ICO_PNG_COLORS	0
38 #define ICO_PNG_PLANES	1
39 #define ICO_PNG_BPP	32
40 
41 typedef struct Ico_header_s
42 {
43 	uint16_t		magic;
44 	uint16_t		type;
45 	uint16_t		count;
46 } Ico_header_t;
47 
48 typedef struct Ico_directory_s
49 {
50 	uint8_t			width;
51 	uint8_t			height;
52 	uint8_t			colors;
53 	uint8_t			magic;
54 	uint16_t		panes;
55 	uint16_t		bpp;
56 	uint32_t		size;
57 	uint32_t		offset;
58 } Ico_directory_t;
59 
60 typedef struct Ico_s
61 {
62 	Sfio_t*			head;
63 	Sfio_t*			data;
64 	unsigned char*		cur;
65 	int			mode;
66 	int			entries;
67 	char			name[32];
68 	unsigned char		dir[1];
69 } Ico_t;
70 
71 static int
ico_done(Pax_t * pax,register Paxarchive_t * ap)72 ico_done(Pax_t* pax, register Paxarchive_t* ap)
73 {
74 	register Ico_t*	ico = (Ico_t*)ap->data;
75 
76 	if (!ico)
77 		return -1;
78 	if (ico->data)
79 		sfclose(ico->data);
80 	if (ico->head)
81 		sfclose(ico->head);
82 	free(ico);
83 	ap->data = 0;
84 	return 0;
85 }
86 
87 static int
ico_getprologue(Pax_t * pax,Paxformat_t * fp,register Paxarchive_t * ap,Paxfile_t * f,register unsigned char * p,size_t size)88 ico_getprologue(Pax_t* pax, Paxformat_t* fp, register Paxarchive_t* ap, Paxfile_t* f, register unsigned char* p, size_t size)
89 {
90 	register Ico_t*	ico;
91 	size_t		m;
92 	size_t		n;
93 
94 	if (size < ICO_HEADER + ICO_DIRECTORY)
95 		return 0;
96 	if (swapget(3, p, 2) != ICO_MAGIC || swapget(3, p+2, 2) != ICO_TYPE || p[9] != 0 && p[9] != 255)
97 		return 0;
98 	if (paxseek(pax, ap, ICO_HEADER, SEEK_SET, 1) != ICO_HEADER)
99 		return 0;
100 	m = swapget(3, p+4, 2);
101 	n = m * ICO_DIRECTORY;
102 	if (!(ico = newof(0, Ico_t, 1, n - 1)))
103 		return paxnospace(pax);
104 	umask(ico->mode = umask(0));
105 	ico->mode = X_IFREG | (0666 & ~ico->mode);
106 	ico->entries = m;
107 	if (paxread(pax, ap, ico->dir, n, n, 0) <= 0)
108 	{
109 		free(ico);
110 		return 0;
111 	}
112 	ico->cur = ico->dir;
113 	ap->data = ico;
114 	return 1;
115 }
116 
117 static int
ico_getheader(Pax_t * pax,register Paxarchive_t * ap,register Paxfile_t * f)118 ico_getheader(Pax_t* pax, register Paxarchive_t* ap, register Paxfile_t* f)
119 {
120 	register Ico_t*			ico = (Ico_t*)ap->data;
121 	register unsigned char*		p;
122 	int				width;
123 	int				height;
124 	size_t				offset;
125 	size_t				size;
126 
127 	if (ap->entry > ico->entries)
128 	{
129 		ap->io->eof = 1;
130 		return 0;
131 	}
132 	p = ico->cur;
133 	ico->cur += ICO_DIRECTORY;
134 	width = p[0] ? p[0] : 256;
135 	height = p[1] ? p[1] : 256;
136 	size = swapget(3, p+8, 4);
137 	offset = swapget(3, p+12, 4);
138 	sfsprintf(ico->name, sizeof(ico->name), "ico-%d-c%dp%db%dw%dh%d.ico", ap->entry, p[2], (int)swapget(3, p+4, 2), (int)swapget(3, p+6, 2), width, height);
139 	f->name = ico->name;
140 	f->linktype = PAX_NOLINK;
141 	f->linkpath = 0;
142 	f->st->st_mode = ico->mode;
143 	f->st->st_uid = pax->uid;
144 	f->st->st_gid = pax->gid;
145 	f->st->st_size = size;
146 	f->st->st_mtime = time(NiL);
147 	f->st->st_nlink = 1;
148 	if (paxseek(pax, ap, offset, SEEK_SET, 1) != offset)
149 		return -1;
150 	return 1;
151 }
152 
153 static int
ico_getdata(Pax_t * pax,register Paxarchive_t * ap,register Paxfile_t * f,int fd)154 ico_getdata(Pax_t* pax, register Paxarchive_t* ap, register Paxfile_t* f, int fd)
155 {
156 	register Ico_t*	ico = (Ico_t*)ap->data;
157 	Sfio_t*		sp;
158 	ssize_t		n;
159 	size_t		z;
160 	unsigned char*	dir;
161 	unsigned char	hdr[ICO_HEADER];
162 
163 	if (fd < 0 || !f->st->st_size)
164 		return 1;
165 	if (!(sp = paxpart(pax, ap, f->st->st_size)))
166 		return -1;
167 	swapput(3, hdr+0, 2, ICO_MAGIC);
168 	swapput(3, hdr+2, 2, ICO_TYPE);
169 	swapput(3, hdr+4, 2, 1);
170 	sfwrite(sp, hdr, ICO_HEADER);
171 	if (paxdata(pax, ap, f, fd, hdr, ICO_HEADER))
172 		return -1;
173 	dir = ico->cur - ICO_DIRECTORY;
174 	swapput(3, dir+12, 4, ICO_HEADER + ICO_DIRECTORY);
175 	if (paxdata(pax, ap, f, fd, dir, ICO_DIRECTORY))
176 		return -1;
177 	while ((n = sfread(sp, pax->buf, sizeof(pax->buf))) > 0)
178 		if (paxdata(pax, ap, f, fd, pax->buf, n))
179 			return -1;
180 	if (n)
181 		(*pax->errorf)(NiL, pax, 2, "%s: %s: unexpected EOF", ap->name, f->name);
182 	return 1;
183 }
184 
185 static int
ico_putprologue(Pax_t * pax,register Paxarchive_t * ap,int append)186 ico_putprologue(Pax_t* pax, register Paxarchive_t* ap, int append)
187 {
188 	register Ico_t*	ico = (Ico_t*)ap->data;
189 	unsigned char	hdr[ICO_HEADER];
190 
191 	if (!ico)
192 	{
193 		if (!(ico = newof(0, Ico_t, 1, 0)) || !(ico->head = sfstropen()) || !(ico->data = sftmp(64 * 1024)))
194 			return paxnospace(pax);
195 		swapput(3, hdr+0, 2, ICO_MAGIC);
196 		swapput(3, hdr+2, 2, ICO_TYPE);
197 		swapput(3, hdr+4, 2, 0);
198 		sfwrite(ico->head, hdr, ICO_HEADER);
199 		ap->data = ico;
200 	}
201 	return 1;
202 }
203 
204 static int
ico_putheader(Pax_t * pax,Paxarchive_t * ap,Paxfile_t * f)205 ico_putheader(Pax_t* pax, Paxarchive_t* ap, Paxfile_t* f)
206 {
207 	return 1;
208 }
209 
210 static int
ico_putdata(Pax_t * pax,Paxarchive_t * ap,Paxfile_t * f,int fd)211 ico_putdata(Pax_t* pax, Paxarchive_t* ap, Paxfile_t* f, int fd)
212 {
213 	register Ico_t*	ico = (Ico_t*)ap->data;
214 	Sfio_t*		sp;
215 	unsigned char*	p;
216 	int		d;
217 	ssize_t		n;
218 	size_t		z;
219 	uint32_t	size;
220 	unsigned char	dir[ICO_HEADER+ICO_DIRECTORY];
221 
222 	if (!(sp = sfnew(NiL, NiL, SF_UNBOUND, fd, SF_READ)))
223 		return paxnospace(pax);
224 	d = 1;
225 	z = f->st->st_size;
226 	do
227 	{
228 		if ((n = sizeof(pax->buf)) > z)
229 			n = z;
230 		if ((n = sfread(sp, pax->buf, n)) <= 0)
231 		{
232 			if (pax->errorf)
233 				(*pax->errorf)(NiL, pax, 2, "%s: %s: read error", ap->name, f->name);
234 			return -1;
235 		}
236 		p = (unsigned char*)pax->buf;
237 		if (d)
238 		{
239 			if (n < ICO_HEADER + ICO_DIRECTORY)
240 			{
241 				if (pax->errorf)
242 					(*pax->errorf)(NiL, pax, 2, "%s: %s: read error", ap->name, f->name);
243 				return -1;
244 			}
245 			if (swapget(3, p+0, 2) == ICO_MAGIC && swapget(3, p+2, 2) == ICO_TYPE)
246 			{
247 				sfwrite(ico->head, p+ICO_HEADER, ICO_DIRECTORY);
248 				p += ICO_HEADER + ICO_DIRECTORY;
249 				n -= ICO_HEADER + ICO_DIRECTORY;
250 				z -= ICO_HEADER + ICO_DIRECTORY;
251 			}
252 			else if (!memcmp(p, "\x89PNG\x0d\x0a\x1a\x0a", 8))
253 			{
254 				dir[0] = (d = swapget(0, p+16, 4)) < 256 ? d : 0;
255 				dir[1] = (d = swapget(0, p+20, 4)) < 256 ? d : 0;
256 				dir[2] = ICO_PNG_COLORS;
257 				dir[3] = 0;
258 				swapput(3, dir+4, 2, ICO_PNG_PLANES);
259 				swapput(3, dir+6, 2, ICO_PNG_BPP);
260 				swapput(3, dir+8, 2, f->st->st_size);
261 				swapput(3, dir+12, 2, 0);
262 				sfwrite(ico->head, dir, ICO_DIRECTORY);
263 			}
264 			else
265 			{
266 				if (pax->errorf)
267 					(*pax->errorf)(NiL, pax, 2, "%s: %s: not an icon resource or image", ap->name, f->name);
268 				return -1;
269 			}
270 			d = 0;
271 		}
272 		sfwrite(ico->data, p, n);
273 	} while (z -= n);
274 	if (sfclose(sp))
275 	{
276 		if (pax->errorf)
277 			(*pax->errorf)(NiL, pax, 2, "%s: %s: write error", ap->name, f->name);
278 		return -1;
279 	}
280 	return n;
281 }
282 
283 static off_t
ico_putepilogue(Pax_t * pax,Paxarchive_t * ap)284 ico_putepilogue(Pax_t* pax, Paxarchive_t* ap)
285 {
286 	register Ico_t*		ico = (Ico_t*)ap->data;
287 	register unsigned char*	p;
288 	unsigned char*		b;
289 	int			i;
290 	uint32_t		size;
291 	uint32_t		offset;
292 
293 	b = p = (unsigned char*)sfstrbase(ico->head);
294 	swapput(3, p+4, 2, ap->entry);
295 	p += ICO_HEADER;
296 	size = offset = ICO_HEADER + ap->entries * ICO_DIRECTORY;
297 	for (i = 0; i < ap->entry; i++)
298 	{
299 		swapput(3, p+12, 4, offset);
300 		offset += swapget(3, p+8, 4);
301 		p += ICO_DIRECTORY;
302 	}
303 	paxwrite(pax, ap, b, size);
304 	sfseek(ico->data, 0, SEEK_SET);
305 	while ((i = sfread(ico->data, pax->buf, sizeof(pax->buf))) > 0)
306 		paxwrite(pax, ap, pax->buf, i);
307 	if (i < 0)
308 	{
309 		if (pax->errorf)
310 			(*pax->errorf)(NiL, pax, 2, "%s: write error", ap->name);
311 		return -1;
312 	}
313 	return 1;
314 }
315 
316 Paxformat_t	pax_ico_format =
317 {
318 	"ico",
319 	0,
320 	"windows icon file",
321 	0,
322 	PAX_ARCHIVE|PAX_NOHARDLINKS|PAX_IN|PAX_OUT,
323 	PAX_DEFBUFFER,
324 	PAX_DEFBLOCKS,
325 	0,
326 	PAXNEXT(ico),
327 	0,
328 	ico_done,
329 	ico_getprologue,
330 	ico_getheader,
331 	ico_getdata,
332 	0,
333 	0,
334 	ico_putprologue,
335 	ico_putheader,
336 	ico_putdata,
337 	0,
338 	ico_putepilogue,
339 };
340 
341 PAXLIB(ico)
342