xref: /netbsd-src/external/bsd/file/dist/src/cdf.c (revision 946379e7b37692fc43f68eb0d1c10daa0a7f3b6c)
1 /*	$NetBSD: cdf.c,v 1.11 2015/01/02 21:15:32 christos Exp $	*/
2 
3 /*-
4  * Copyright (c) 2008 Christos Zoulas
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28 /*
29  * Parse Composite Document Files, the format used in Microsoft Office
30  * document files before they switched to zipped XML.
31  * Info from: http://sc.openoffice.org/compdocfileformat.pdf
32  *
33  * N.B. This is the "Composite Document File" format, and not the
34  * "Compound Document Format", nor the "Channel Definition Format".
35  */
36 
37 #include "file.h"
38 
39 #ifndef lint
40 #if 0
41 FILE_RCSID("@(#)$File: cdf.c,v 1.69 2014/12/04 15:56:46 christos Exp $")
42 #else
43 __RCSID("$NetBSD: cdf.c,v 1.11 2015/01/02 21:15:32 christos Exp $");
44 #endif
45 #endif
46 
47 #include <assert.h>
48 #ifdef CDF_DEBUG
49 #include <err.h>
50 #endif
51 #include <stdlib.h>
52 #include <unistd.h>
53 #include <string.h>
54 #include <time.h>
55 #include <ctype.h>
56 #ifdef HAVE_LIMITS_H
57 #include <limits.h>
58 #endif
59 
60 #ifndef EFTYPE
61 #define EFTYPE EINVAL
62 #endif
63 
64 #include "cdf.h"
65 
66 #ifdef CDF_DEBUG
67 #define DPRINTF(a) printf a, fflush(stdout)
68 #else
69 #define DPRINTF(a)
70 #endif
71 
72 static union {
73 	char s[4];
74 	uint32_t u;
75 } cdf_bo;
76 
77 #define NEED_SWAP	(cdf_bo.u == (uint32_t)0x01020304)
78 
79 #define CDF_TOLE8(x)	((uint64_t)(NEED_SWAP ? _cdf_tole8(x) : (uint64_t)(x)))
80 #define CDF_TOLE4(x)	((uint32_t)(NEED_SWAP ? _cdf_tole4(x) : (uint32_t)(x)))
81 #define CDF_TOLE2(x)	((uint16_t)(NEED_SWAP ? _cdf_tole2(x) : (uint16_t)(x)))
82 #define CDF_TOLE(x)	(/*CONSTCOND*/sizeof(x) == 2 ? \
83 			    CDF_TOLE2(CAST(uint16_t, x)) : \
84 			(/*CONSTCOND*/sizeof(x) == 4 ? \
85 			    CDF_TOLE4(CAST(uint32_t, x)) : \
86 			    CDF_TOLE8(CAST(uint64_t, x))))
87 #define CDF_GETUINT32(x, y)	cdf_getuint32(x, y)
88 
89 
90 /*
91  * swap a short
92  */
93 static uint16_t
94 _cdf_tole2(uint16_t sv)
95 {
96 	uint16_t rv;
97 	uint8_t *s = (uint8_t *)(void *)&sv;
98 	uint8_t *d = (uint8_t *)(void *)&rv;
99 	d[0] = s[1];
100 	d[1] = s[0];
101 	return rv;
102 }
103 
104 /*
105  * swap an int
106  */
107 static uint32_t
108 _cdf_tole4(uint32_t sv)
109 {
110 	uint32_t rv;
111 	uint8_t *s = (uint8_t *)(void *)&sv;
112 	uint8_t *d = (uint8_t *)(void *)&rv;
113 	d[0] = s[3];
114 	d[1] = s[2];
115 	d[2] = s[1];
116 	d[3] = s[0];
117 	return rv;
118 }
119 
120 /*
121  * swap a quad
122  */
123 static uint64_t
124 _cdf_tole8(uint64_t sv)
125 {
126 	uint64_t rv;
127 	uint8_t *s = (uint8_t *)(void *)&sv;
128 	uint8_t *d = (uint8_t *)(void *)&rv;
129 	d[0] = s[7];
130 	d[1] = s[6];
131 	d[2] = s[5];
132 	d[3] = s[4];
133 	d[4] = s[3];
134 	d[5] = s[2];
135 	d[6] = s[1];
136 	d[7] = s[0];
137 	return rv;
138 }
139 
140 /*
141  * grab a uint32_t from a possibly unaligned address, and return it in
142  * the native host order.
143  */
144 static uint32_t
145 cdf_getuint32(const uint8_t *p, size_t offs)
146 {
147 	uint32_t rv;
148 	(void)memcpy(&rv, p + offs * sizeof(uint32_t), sizeof(rv));
149 	return CDF_TOLE4(rv);
150 }
151 
152 #define CDF_UNPACK(a)	\
153     (void)memcpy(&(a), &buf[len], sizeof(a)), len += sizeof(a)
154 #define CDF_UNPACKA(a)	\
155     (void)memcpy((a), &buf[len], sizeof(a)), len += sizeof(a)
156 
157 uint16_t
158 cdf_tole2(uint16_t sv)
159 {
160 	return CDF_TOLE2(sv);
161 }
162 
163 uint32_t
164 cdf_tole4(uint32_t sv)
165 {
166 	return CDF_TOLE4(sv);
167 }
168 
169 uint64_t
170 cdf_tole8(uint64_t sv)
171 {
172 	return CDF_TOLE8(sv);
173 }
174 
175 void
176 cdf_swap_header(cdf_header_t *h)
177 {
178 	size_t i;
179 
180 	h->h_magic = CDF_TOLE8(h->h_magic);
181 	h->h_uuid[0] = CDF_TOLE8(h->h_uuid[0]);
182 	h->h_uuid[1] = CDF_TOLE8(h->h_uuid[1]);
183 	h->h_revision = CDF_TOLE2(h->h_revision);
184 	h->h_version = CDF_TOLE2(h->h_version);
185 	h->h_byte_order = CDF_TOLE2(h->h_byte_order);
186 	h->h_sec_size_p2 = CDF_TOLE2(h->h_sec_size_p2);
187 	h->h_short_sec_size_p2 = CDF_TOLE2(h->h_short_sec_size_p2);
188 	h->h_num_sectors_in_sat = CDF_TOLE4(h->h_num_sectors_in_sat);
189 	h->h_secid_first_directory = CDF_TOLE4(h->h_secid_first_directory);
190 	h->h_min_size_standard_stream =
191 	    CDF_TOLE4(h->h_min_size_standard_stream);
192 	h->h_secid_first_sector_in_short_sat =
193 	    CDF_TOLE4((uint32_t)h->h_secid_first_sector_in_short_sat);
194 	h->h_num_sectors_in_short_sat =
195 	    CDF_TOLE4(h->h_num_sectors_in_short_sat);
196 	h->h_secid_first_sector_in_master_sat =
197 	    CDF_TOLE4((uint32_t)h->h_secid_first_sector_in_master_sat);
198 	h->h_num_sectors_in_master_sat =
199 	    CDF_TOLE4(h->h_num_sectors_in_master_sat);
200 	for (i = 0; i < __arraycount(h->h_master_sat); i++)
201 		h->h_master_sat[i] = CDF_TOLE4((uint32_t)h->h_master_sat[i]);
202 }
203 
204 void
205 cdf_unpack_header(cdf_header_t *h, char *buf)
206 {
207 	size_t i;
208 	size_t len = 0;
209 
210 	CDF_UNPACK(h->h_magic);
211 	CDF_UNPACKA(h->h_uuid);
212 	CDF_UNPACK(h->h_revision);
213 	CDF_UNPACK(h->h_version);
214 	CDF_UNPACK(h->h_byte_order);
215 	CDF_UNPACK(h->h_sec_size_p2);
216 	CDF_UNPACK(h->h_short_sec_size_p2);
217 	CDF_UNPACKA(h->h_unused0);
218 	CDF_UNPACK(h->h_num_sectors_in_sat);
219 	CDF_UNPACK(h->h_secid_first_directory);
220 	CDF_UNPACKA(h->h_unused1);
221 	CDF_UNPACK(h->h_min_size_standard_stream);
222 	CDF_UNPACK(h->h_secid_first_sector_in_short_sat);
223 	CDF_UNPACK(h->h_num_sectors_in_short_sat);
224 	CDF_UNPACK(h->h_secid_first_sector_in_master_sat);
225 	CDF_UNPACK(h->h_num_sectors_in_master_sat);
226 	for (i = 0; i < __arraycount(h->h_master_sat); i++)
227 		CDF_UNPACK(h->h_master_sat[i]);
228 }
229 
230 void
231 cdf_swap_dir(cdf_directory_t *d)
232 {
233 	d->d_namelen = CDF_TOLE2(d->d_namelen);
234 	d->d_left_child = CDF_TOLE4((uint32_t)d->d_left_child);
235 	d->d_right_child = CDF_TOLE4((uint32_t)d->d_right_child);
236 	d->d_storage = CDF_TOLE4((uint32_t)d->d_storage);
237 	d->d_storage_uuid[0] = CDF_TOLE8(d->d_storage_uuid[0]);
238 	d->d_storage_uuid[1] = CDF_TOLE8(d->d_storage_uuid[1]);
239 	d->d_flags = CDF_TOLE4(d->d_flags);
240 	d->d_created = CDF_TOLE8((uint64_t)d->d_created);
241 	d->d_modified = CDF_TOLE8((uint64_t)d->d_modified);
242 	d->d_stream_first_sector = CDF_TOLE4((uint32_t)d->d_stream_first_sector);
243 	d->d_size = CDF_TOLE4(d->d_size);
244 }
245 
246 void
247 cdf_swap_class(cdf_classid_t *d)
248 {
249 	d->cl_dword = CDF_TOLE4(d->cl_dword);
250 	d->cl_word[0] = CDF_TOLE2(d->cl_word[0]);
251 	d->cl_word[1] = CDF_TOLE2(d->cl_word[1]);
252 }
253 
254 void
255 cdf_unpack_dir(cdf_directory_t *d, char *buf)
256 {
257 	size_t len = 0;
258 
259 	CDF_UNPACKA(d->d_name);
260 	CDF_UNPACK(d->d_namelen);
261 	CDF_UNPACK(d->d_type);
262 	CDF_UNPACK(d->d_color);
263 	CDF_UNPACK(d->d_left_child);
264 	CDF_UNPACK(d->d_right_child);
265 	CDF_UNPACK(d->d_storage);
266 	CDF_UNPACKA(d->d_storage_uuid);
267 	CDF_UNPACK(d->d_flags);
268 	CDF_UNPACK(d->d_created);
269 	CDF_UNPACK(d->d_modified);
270 	CDF_UNPACK(d->d_stream_first_sector);
271 	CDF_UNPACK(d->d_size);
272 	CDF_UNPACK(d->d_unused0);
273 }
274 
275 static int
276 cdf_check_stream_offset(const cdf_stream_t *sst, const cdf_header_t *h,
277     const void *p, size_t tail, int line)
278 {
279 	const char *b = (const char *)sst->sst_tab;
280 	const char *e = ((const char *)p) + tail;
281 	size_t ss = sst->sst_dirlen < h->h_min_size_standard_stream ?
282 	    CDF_SHORT_SEC_SIZE(h) : CDF_SEC_SIZE(h);
283 	/*LINTED*/(void)&line;
284 	if (e >= b && (size_t)(e - b) <= ss * sst->sst_len)
285 		return 0;
286 	DPRINTF(("%d: offset begin %p < end %p || %" SIZE_T_FORMAT "u"
287 	    " > %" SIZE_T_FORMAT "u [%" SIZE_T_FORMAT "u %"
288 	    SIZE_T_FORMAT "u]\n", line, b, e, (size_t)(e - b),
289 	    ss * sst->sst_len, ss, sst->sst_len));
290 	errno = EFTYPE;
291 	return -1;
292 }
293 
294 static ssize_t
295 cdf_read(const cdf_info_t *info, off_t off, void *buf, size_t len)
296 {
297 	size_t siz = (size_t)off + len;
298 
299 	if ((off_t)(off + len) != (off_t)siz) {
300 		errno = EINVAL;
301 		return -1;
302 	}
303 
304 	if (info->i_buf != NULL && info->i_len >= siz) {
305 		(void)memcpy(buf, &info->i_buf[off], len);
306 		return (ssize_t)len;
307 	}
308 
309 	if (info->i_fd == -1)
310 		return -1;
311 
312 	if (pread(info->i_fd, buf, len, off) != (ssize_t)len)
313 		return -1;
314 
315 	return (ssize_t)len;
316 }
317 
318 int
319 cdf_read_header(const cdf_info_t *info, cdf_header_t *h)
320 {
321 	char buf[512];
322 
323 	(void)memcpy(cdf_bo.s, "\01\02\03\04", 4);
324 	if (cdf_read(info, (off_t)0, buf, sizeof(buf)) == -1)
325 		return -1;
326 	cdf_unpack_header(h, buf);
327 	cdf_swap_header(h);
328 	if (h->h_magic != CDF_MAGIC) {
329 		DPRINTF(("Bad magic 0x%" INT64_T_FORMAT "x != 0x%"
330 		    INT64_T_FORMAT "x\n",
331 		    (unsigned long long)h->h_magic,
332 		    (unsigned long long)CDF_MAGIC));
333 		goto out;
334 	}
335 	if (h->h_sec_size_p2 > 20) {
336 		DPRINTF(("Bad sector size 0x%u\n", h->h_sec_size_p2));
337 		goto out;
338 	}
339 	if (h->h_short_sec_size_p2 > 20) {
340 		DPRINTF(("Bad short sector size 0x%u\n",
341 		    h->h_short_sec_size_p2));
342 		goto out;
343 	}
344 	return 0;
345 out:
346 	errno = EFTYPE;
347 	return -1;
348 }
349 
350 
351 ssize_t
352 cdf_read_sector(const cdf_info_t *info, void *buf, size_t offs, size_t len,
353     const cdf_header_t *h, cdf_secid_t id)
354 {
355 	size_t ss = CDF_SEC_SIZE(h);
356 	size_t pos = CDF_SEC_POS(h, id);
357 	assert(ss == len);
358 	return cdf_read(info, (off_t)pos, ((char *)buf) + offs, len);
359 }
360 
361 ssize_t
362 cdf_read_short_sector(const cdf_stream_t *sst, void *buf, size_t offs,
363     size_t len, const cdf_header_t *h, cdf_secid_t id)
364 {
365 	size_t ss = CDF_SHORT_SEC_SIZE(h);
366 	size_t pos = CDF_SHORT_SEC_POS(h, id);
367 	assert(ss == len);
368 	if (pos + len > CDF_SEC_SIZE(h) * sst->sst_len) {
369 		DPRINTF(("Out of bounds read %" SIZE_T_FORMAT "u > %"
370 		    SIZE_T_FORMAT "u\n",
371 		    pos + len, CDF_SEC_SIZE(h) * sst->sst_len));
372 		return -1;
373 	}
374 	(void)memcpy(((char *)buf) + offs,
375 	    ((const char *)sst->sst_tab) + pos, len);
376 	return len;
377 }
378 
379 /*
380  * Read the sector allocation table.
381  */
382 int
383 cdf_read_sat(const cdf_info_t *info, cdf_header_t *h, cdf_sat_t *sat)
384 {
385 	size_t i, j, k;
386 	size_t ss = CDF_SEC_SIZE(h);
387 	cdf_secid_t *msa, mid, sec;
388 	size_t nsatpersec = (ss / sizeof(mid)) - 1;
389 
390 	for (i = 0; i < __arraycount(h->h_master_sat); i++)
391 		if (h->h_master_sat[i] == CDF_SECID_FREE)
392 			break;
393 
394 #define CDF_SEC_LIMIT (UINT32_MAX / (4 * ss))
395 	if ((nsatpersec > 0 &&
396 	    h->h_num_sectors_in_master_sat > CDF_SEC_LIMIT / nsatpersec) ||
397 	    i > CDF_SEC_LIMIT) {
398 		DPRINTF(("Number of sectors in master SAT too big %u %"
399 		    SIZE_T_FORMAT "u\n", h->h_num_sectors_in_master_sat, i));
400 		errno = EFTYPE;
401 		return -1;
402 	}
403 
404 	sat->sat_len = h->h_num_sectors_in_master_sat * nsatpersec + i;
405 	DPRINTF(("sat_len = %" SIZE_T_FORMAT "u ss = %" SIZE_T_FORMAT "u\n",
406 	    sat->sat_len, ss));
407 	if ((sat->sat_tab = CAST(cdf_secid_t *, calloc(sat->sat_len, ss)))
408 	    == NULL)
409 		return -1;
410 
411 	for (i = 0; i < __arraycount(h->h_master_sat); i++) {
412 		if (h->h_master_sat[i] < 0)
413 			break;
414 		if (cdf_read_sector(info, sat->sat_tab, ss * i, ss, h,
415 		    h->h_master_sat[i]) != (ssize_t)ss) {
416 			DPRINTF(("Reading sector %d", h->h_master_sat[i]));
417 			goto out1;
418 		}
419 	}
420 
421 	if ((msa = CAST(cdf_secid_t *, calloc(1, ss))) == NULL)
422 		goto out1;
423 
424 	mid = h->h_secid_first_sector_in_master_sat;
425 	for (j = 0; j < h->h_num_sectors_in_master_sat; j++) {
426 		if (mid < 0)
427 			goto out;
428 		if (j >= CDF_LOOP_LIMIT) {
429 			DPRINTF(("Reading master sector loop limit"));
430 			errno = EFTYPE;
431 			goto out2;
432 		}
433 		if (cdf_read_sector(info, msa, 0, ss, h, mid) != (ssize_t)ss) {
434 			DPRINTF(("Reading master sector %d", mid));
435 			goto out2;
436 		}
437 		for (k = 0; k < nsatpersec; k++, i++) {
438 			sec = CDF_TOLE4((uint32_t)msa[k]);
439 			if (sec < 0)
440 				goto out;
441 			if (i >= sat->sat_len) {
442 			    DPRINTF(("Out of bounds reading MSA %" SIZE_T_FORMAT
443 				"u >= %" SIZE_T_FORMAT "u", i, sat->sat_len));
444 			    errno = EFTYPE;
445 			    goto out2;
446 			}
447 			if (cdf_read_sector(info, sat->sat_tab, ss * i, ss, h,
448 			    sec) != (ssize_t)ss) {
449 				DPRINTF(("Reading sector %d",
450 				    CDF_TOLE4(msa[k])));
451 				goto out2;
452 			}
453 		}
454 		mid = CDF_TOLE4((uint32_t)msa[nsatpersec]);
455 	}
456 out:
457 	sat->sat_len = i;
458 	free(msa);
459 	return 0;
460 out2:
461 	free(msa);
462 out1:
463 	free(sat->sat_tab);
464 	return -1;
465 }
466 
467 size_t
468 cdf_count_chain(const cdf_sat_t *sat, cdf_secid_t sid, size_t size)
469 {
470 	size_t i, j;
471 	cdf_secid_t maxsector = (cdf_secid_t)((sat->sat_len * size)
472 	    / sizeof(maxsector));
473 
474 	DPRINTF(("Chain:"));
475 	if (sid == CDF_SECID_END_OF_CHAIN) {
476 		/* 0-length chain. */
477 		DPRINTF((" empty\n"));
478 		return 0;
479 	}
480 
481 	for (j = i = 0; sid >= 0; i++, j++) {
482 		DPRINTF((" %d", sid));
483 		if (j >= CDF_LOOP_LIMIT) {
484 			DPRINTF(("Counting chain loop limit"));
485 			errno = EFTYPE;
486 			return (size_t)-1;
487 		}
488 		if (sid >= maxsector) {
489 			DPRINTF(("Sector %d >= %d\n", sid, maxsector));
490 			errno = EFTYPE;
491 			return (size_t)-1;
492 		}
493 		sid = CDF_TOLE4((uint32_t)sat->sat_tab[sid]);
494 	}
495 	if (i == 0) {
496 		DPRINTF((" none, sid: %d\n", sid));
497 		return (size_t)-1;
498 
499 	}
500 	DPRINTF(("\n"));
501 	return i;
502 }
503 
504 int
505 cdf_read_long_sector_chain(const cdf_info_t *info, const cdf_header_t *h,
506     const cdf_sat_t *sat, cdf_secid_t sid, size_t len, cdf_stream_t *scn)
507 {
508 	size_t ss = CDF_SEC_SIZE(h), i, j;
509 	ssize_t nr;
510 	scn->sst_len = cdf_count_chain(sat, sid, ss);
511 	scn->sst_dirlen = len;
512 
513 	if (scn->sst_len == (size_t)-1)
514 		return -1;
515 
516 	scn->sst_tab = calloc(scn->sst_len, ss);
517 	if (scn->sst_tab == NULL)
518 		return -1;
519 
520 	for (j = i = 0; sid >= 0; i++, j++) {
521 		if (j >= CDF_LOOP_LIMIT) {
522 			DPRINTF(("Read long sector chain loop limit"));
523 			errno = EFTYPE;
524 			goto out;
525 		}
526 		if (i >= scn->sst_len) {
527 			DPRINTF(("Out of bounds reading long sector chain "
528 			    "%" SIZE_T_FORMAT "u > %" SIZE_T_FORMAT "u\n", i,
529 			    scn->sst_len));
530 			errno = EFTYPE;
531 			goto out;
532 		}
533 		if ((nr = cdf_read_sector(info, scn->sst_tab, i * ss, ss, h,
534 		    sid)) != (ssize_t)ss) {
535 			if (i == scn->sst_len - 1 && nr > 0) {
536 				/* Last sector might be truncated */
537 				return 0;
538 			}
539 			DPRINTF(("Reading long sector chain %d", sid));
540 			goto out;
541 		}
542 		sid = CDF_TOLE4((uint32_t)sat->sat_tab[sid]);
543 	}
544 	return 0;
545 out:
546 	free(scn->sst_tab);
547 	return -1;
548 }
549 
550 int
551 cdf_read_short_sector_chain(const cdf_header_t *h,
552     const cdf_sat_t *ssat, const cdf_stream_t *sst,
553     cdf_secid_t sid, size_t len, cdf_stream_t *scn)
554 {
555 	size_t ss = CDF_SHORT_SEC_SIZE(h), i, j;
556 	scn->sst_len = cdf_count_chain(ssat, sid, CDF_SEC_SIZE(h));
557 	scn->sst_dirlen = len;
558 
559 	if (sst->sst_tab == NULL || scn->sst_len == (size_t)-1)
560 		return -1;
561 
562 	scn->sst_tab = calloc(scn->sst_len, ss);
563 	if (scn->sst_tab == NULL)
564 		return -1;
565 
566 	for (j = i = 0; sid >= 0; i++, j++) {
567 		if (j >= CDF_LOOP_LIMIT) {
568 			DPRINTF(("Read short sector chain loop limit"));
569 			errno = EFTYPE;
570 			goto out;
571 		}
572 		if (i >= scn->sst_len) {
573 			DPRINTF(("Out of bounds reading short sector chain "
574 			    "%" SIZE_T_FORMAT "u > %" SIZE_T_FORMAT "u\n",
575 			    i, scn->sst_len));
576 			errno = EFTYPE;
577 			goto out;
578 		}
579 		if (cdf_read_short_sector(sst, scn->sst_tab, i * ss, ss, h,
580 		    sid) != (ssize_t)ss) {
581 			DPRINTF(("Reading short sector chain %d", sid));
582 			goto out;
583 		}
584 		sid = CDF_TOLE4((uint32_t)ssat->sat_tab[sid]);
585 	}
586 	return 0;
587 out:
588 	free(scn->sst_tab);
589 	return -1;
590 }
591 
592 int
593 cdf_read_sector_chain(const cdf_info_t *info, const cdf_header_t *h,
594     const cdf_sat_t *sat, const cdf_sat_t *ssat, const cdf_stream_t *sst,
595     cdf_secid_t sid, size_t len, cdf_stream_t *scn)
596 {
597 
598 	if (len < h->h_min_size_standard_stream && sst->sst_tab != NULL)
599 		return cdf_read_short_sector_chain(h, ssat, sst, sid, len,
600 		    scn);
601 	else
602 		return cdf_read_long_sector_chain(info, h, sat, sid, len, scn);
603 }
604 
605 int
606 cdf_read_dir(const cdf_info_t *info, const cdf_header_t *h,
607     const cdf_sat_t *sat, cdf_dir_t *dir)
608 {
609 	size_t i, j;
610 	size_t ss = CDF_SEC_SIZE(h), ns, nd;
611 	char *buf;
612 	cdf_secid_t sid = h->h_secid_first_directory;
613 
614 	ns = cdf_count_chain(sat, sid, ss);
615 	if (ns == (size_t)-1)
616 		return -1;
617 
618 	nd = ss / CDF_DIRECTORY_SIZE;
619 
620 	dir->dir_len = ns * nd;
621 	dir->dir_tab = CAST(cdf_directory_t *,
622 	    calloc(dir->dir_len, sizeof(dir->dir_tab[0])));
623 	if (dir->dir_tab == NULL)
624 		return -1;
625 
626 	if ((buf = CAST(char *, malloc(ss))) == NULL) {
627 		free(dir->dir_tab);
628 		return -1;
629 	}
630 
631 	for (j = i = 0; i < ns; i++, j++) {
632 		if (j >= CDF_LOOP_LIMIT) {
633 			DPRINTF(("Read dir loop limit"));
634 			errno = EFTYPE;
635 			goto out;
636 		}
637 		if (cdf_read_sector(info, buf, 0, ss, h, sid) != (ssize_t)ss) {
638 			DPRINTF(("Reading directory sector %d", sid));
639 			goto out;
640 		}
641 		for (j = 0; j < nd; j++) {
642 			cdf_unpack_dir(&dir->dir_tab[i * nd + j],
643 			    &buf[j * CDF_DIRECTORY_SIZE]);
644 		}
645 		sid = CDF_TOLE4((uint32_t)sat->sat_tab[sid]);
646 	}
647 	if (NEED_SWAP)
648 		for (i = 0; i < dir->dir_len; i++)
649 			cdf_swap_dir(&dir->dir_tab[i]);
650 	free(buf);
651 	return 0;
652 out:
653 	free(dir->dir_tab);
654 	free(buf);
655 	return -1;
656 }
657 
658 
659 int
660 cdf_read_ssat(const cdf_info_t *info, const cdf_header_t *h,
661     const cdf_sat_t *sat, cdf_sat_t *ssat)
662 {
663 	size_t i, j;
664 	size_t ss = CDF_SEC_SIZE(h);
665 	cdf_secid_t sid = h->h_secid_first_sector_in_short_sat;
666 
667 	ssat->sat_len = cdf_count_chain(sat, sid, CDF_SEC_SIZE(h));
668 	if (ssat->sat_len == (size_t)-1)
669 		return -1;
670 
671 	ssat->sat_tab = CAST(cdf_secid_t *, calloc(ssat->sat_len, ss));
672 	if (ssat->sat_tab == NULL)
673 		return -1;
674 
675 	for (j = i = 0; sid >= 0; i++, j++) {
676 		if (j >= CDF_LOOP_LIMIT) {
677 			DPRINTF(("Read short sat sector loop limit"));
678 			errno = EFTYPE;
679 			goto out;
680 		}
681 		if (i >= ssat->sat_len) {
682 			DPRINTF(("Out of bounds reading short sector chain "
683 			    "%" SIZE_T_FORMAT "u > %" SIZE_T_FORMAT "u\n", i,
684 			    ssat->sat_len));
685 			errno = EFTYPE;
686 			goto out;
687 		}
688 		if (cdf_read_sector(info, ssat->sat_tab, i * ss, ss, h, sid) !=
689 		    (ssize_t)ss) {
690 			DPRINTF(("Reading short sat sector %d", sid));
691 			goto out;
692 		}
693 		sid = CDF_TOLE4((uint32_t)sat->sat_tab[sid]);
694 	}
695 	return 0;
696 out:
697 	free(ssat->sat_tab);
698 	return -1;
699 }
700 
701 int
702 cdf_read_short_stream(const cdf_info_t *info, const cdf_header_t *h,
703     const cdf_sat_t *sat, const cdf_dir_t *dir, cdf_stream_t *scn,
704     const cdf_directory_t **root)
705 {
706 	size_t i;
707 	const cdf_directory_t *d;
708 
709 	*root = NULL;
710 	for (i = 0; i < dir->dir_len; i++)
711 		if (dir->dir_tab[i].d_type == CDF_DIR_TYPE_ROOT_STORAGE)
712 			break;
713 
714 	/* If the it is not there, just fake it; some docs don't have it */
715 	if (i == dir->dir_len)
716 		goto out;
717 	d = &dir->dir_tab[i];
718 	*root = d;
719 
720 	/* If the it is not there, just fake it; some docs don't have it */
721 	if (d->d_stream_first_sector < 0)
722 		goto out;
723 
724 	return	cdf_read_long_sector_chain(info, h, sat,
725 	    d->d_stream_first_sector, d->d_size, scn);
726 out:
727 	scn->sst_tab = NULL;
728 	scn->sst_len = 0;
729 	scn->sst_dirlen = 0;
730 	return 0;
731 }
732 
733 static int
734 cdf_namecmp(const char *d, const uint16_t *s, size_t l)
735 {
736 	for (; l--; d++, s++)
737 		if (*d != CDF_TOLE2(*s))
738 			return (unsigned char)*d - CDF_TOLE2(*s);
739 	return 0;
740 }
741 
742 int
743 cdf_read_summary_info(const cdf_info_t *info, const cdf_header_t *h,
744     const cdf_sat_t *sat, const cdf_sat_t *ssat, const cdf_stream_t *sst,
745     const cdf_dir_t *dir, cdf_stream_t *scn)
746 {
747 	return cdf_read_user_stream(info, h, sat, ssat, sst, dir,
748 	    "\05SummaryInformation", scn);
749 }
750 
751 int
752 cdf_read_user_stream(const cdf_info_t *info, const cdf_header_t *h,
753     const cdf_sat_t *sat, const cdf_sat_t *ssat, const cdf_stream_t *sst,
754     const cdf_dir_t *dir, const char *name, cdf_stream_t *scn)
755 {
756 	size_t i;
757 	const cdf_directory_t *d;
758 	size_t name_len = strlen(name) + 1;
759 
760 	for (i = dir->dir_len; i > 0; i--)
761 		if (dir->dir_tab[i - 1].d_type == CDF_DIR_TYPE_USER_STREAM &&
762 		    cdf_namecmp(name, dir->dir_tab[i - 1].d_name, name_len)
763 		    == 0)
764 			break;
765 
766 	if (i == 0) {
767 		DPRINTF(("Cannot find user stream `%s'\n", name));
768 		errno = ESRCH;
769 		return -1;
770 	}
771 	d = &dir->dir_tab[i - 1];
772 	return cdf_read_sector_chain(info, h, sat, ssat, sst,
773 	    d->d_stream_first_sector, d->d_size, scn);
774 }
775 
776 int
777 cdf_read_property_info(const cdf_stream_t *sst, const cdf_header_t *h,
778     uint32_t offs, cdf_property_info_t **info, size_t *count, size_t *maxcount)
779 {
780 	const cdf_section_header_t *shp;
781 	cdf_section_header_t sh;
782 	const uint8_t *p, *q, *e;
783 	int16_t s16;
784 	int32_t s32;
785 	uint32_t u32;
786 	int64_t s64;
787 	uint64_t u64;
788 	cdf_timestamp_t tp;
789 	size_t i, o, o4, nelements, j;
790 	cdf_property_info_t *inp;
791 
792 	if (offs > UINT32_MAX / 4) {
793 		errno = EFTYPE;
794 		goto out;
795 	}
796 	shp = CAST(const cdf_section_header_t *, (const void *)
797 	    ((const char *)sst->sst_tab + offs));
798 	if (cdf_check_stream_offset(sst, h, shp, sizeof(*shp), __LINE__) == -1)
799 		goto out;
800 	sh.sh_len = CDF_TOLE4(shp->sh_len);
801 #define CDF_SHLEN_LIMIT (UINT32_MAX / 8)
802 	if (sh.sh_len > CDF_SHLEN_LIMIT) {
803 		errno = EFTYPE;
804 		goto out;
805 	}
806 	sh.sh_properties = CDF_TOLE4(shp->sh_properties);
807 #define CDF_PROP_LIMIT (UINT32_MAX / (4 * sizeof(*inp)))
808 	if (sh.sh_properties > CDF_PROP_LIMIT)
809 		goto out;
810 	DPRINTF(("section len: %u properties %u\n", sh.sh_len,
811 	    sh.sh_properties));
812 	if (*maxcount) {
813 		if (*maxcount > CDF_PROP_LIMIT)
814 			goto out;
815 		*maxcount += sh.sh_properties;
816 		inp = CAST(cdf_property_info_t *,
817 		    realloc(*info, *maxcount * sizeof(*inp)));
818 	} else {
819 		*maxcount = sh.sh_properties;
820 		inp = CAST(cdf_property_info_t *,
821 		    malloc(*maxcount * sizeof(*inp)));
822 	}
823 	if (inp == NULL)
824 		goto out;
825 	*info = inp;
826 	inp += *count;
827 	*count += sh.sh_properties;
828 	p = CAST(const uint8_t *, (const void *)
829 	    ((const char *)(const void *)sst->sst_tab +
830 	    offs + sizeof(sh)));
831 	e = CAST(const uint8_t *, (const void *)
832 	    (((const char *)(const void *)shp) + sh.sh_len));
833 	if (cdf_check_stream_offset(sst, h, e, 0, __LINE__) == -1)
834 		goto out;
835 	for (i = 0; i < sh.sh_properties; i++) {
836 		size_t tail = (i << 1) + 1;
837 		size_t ofs;
838 		if (cdf_check_stream_offset(sst, h, p, tail * sizeof(uint32_t),
839 		    __LINE__) == -1)
840 			goto out;
841 		ofs = CDF_GETUINT32(p, tail);
842 		q = (const uint8_t *)(const void *)
843 		    ((const char *)(const void *)p + ofs
844 		    - 2 * sizeof(uint32_t));
845 		if (q < p) {
846 			DPRINTF(("Wrapped around %p < %p\n", q, p));
847 			goto out;
848 		}
849 		if (q > e) {
850 			DPRINTF(("Ran of the end %p > %p\n", q, e));
851 			goto out;
852 		}
853 		inp[i].pi_id = CDF_GETUINT32(p, i << 1);
854 		inp[i].pi_type = CDF_GETUINT32(q, 0);
855 		DPRINTF(("%" SIZE_T_FORMAT "u) id=%x type=%x offs=0x%tx,0x%x\n",
856 		    i, inp[i].pi_id, inp[i].pi_type, q - p, offs));
857 		if (inp[i].pi_type & CDF_VECTOR) {
858 			nelements = CDF_GETUINT32(q, 1);
859 			if (nelements == 0) {
860 				DPRINTF(("CDF_VECTOR with nelements == 0\n"));
861 				goto out;
862 			}
863 			o = 2;
864 		} else {
865 			nelements = 1;
866 			o = 1;
867 		}
868 		o4 = o * sizeof(uint32_t);
869 		if (inp[i].pi_type & (CDF_ARRAY|CDF_BYREF|CDF_RESERVED))
870 			goto unknown;
871 		switch (inp[i].pi_type & CDF_TYPEMASK) {
872 		case CDF_NULL:
873 		case CDF_EMPTY:
874 			break;
875 		case CDF_SIGNED16:
876 			if (inp[i].pi_type & CDF_VECTOR)
877 				goto unknown;
878 			(void)memcpy(&s16, &q[o4], sizeof(s16));
879 			inp[i].pi_s16 = CDF_TOLE2(s16);
880 			break;
881 		case CDF_SIGNED32:
882 			if (inp[i].pi_type & CDF_VECTOR)
883 				goto unknown;
884 			(void)memcpy(&s32, &q[o4], sizeof(s32));
885 			inp[i].pi_s32 = CDF_TOLE4((uint32_t)s32);
886 			break;
887 		case CDF_BOOL:
888 		case CDF_UNSIGNED32:
889 			if (inp[i].pi_type & CDF_VECTOR)
890 				goto unknown;
891 			(void)memcpy(&u32, &q[o4], sizeof(u32));
892 			inp[i].pi_u32 = CDF_TOLE4(u32);
893 			break;
894 		case CDF_SIGNED64:
895 			if (inp[i].pi_type & CDF_VECTOR)
896 				goto unknown;
897 			(void)memcpy(&s64, &q[o4], sizeof(s64));
898 			inp[i].pi_s64 = CDF_TOLE8((uint64_t)s64);
899 			break;
900 		case CDF_UNSIGNED64:
901 			if (inp[i].pi_type & CDF_VECTOR)
902 				goto unknown;
903 			(void)memcpy(&u64, &q[o4], sizeof(u64));
904 			inp[i].pi_u64 = CDF_TOLE8((uint64_t)u64);
905 			break;
906 		case CDF_FLOAT:
907 			if (inp[i].pi_type & CDF_VECTOR)
908 				goto unknown;
909 			(void)memcpy(&u32, &q[o4], sizeof(u32));
910 			u32 = CDF_TOLE4(u32);
911 			memcpy(&inp[i].pi_f, &u32, sizeof(inp[i].pi_f));
912 			break;
913 		case CDF_DOUBLE:
914 			if (inp[i].pi_type & CDF_VECTOR)
915 				goto unknown;
916 			(void)memcpy(&u64, &q[o4], sizeof(u64));
917 			u64 = CDF_TOLE8((uint64_t)u64);
918 			memcpy(&inp[i].pi_d, &u64, sizeof(inp[i].pi_d));
919 			break;
920 		case CDF_LENGTH32_STRING:
921 		case CDF_LENGTH32_WSTRING:
922 			if (nelements > 1) {
923 				size_t nelem = inp - *info;
924 				if (*maxcount > CDF_PROP_LIMIT
925 				    || nelements > CDF_PROP_LIMIT)
926 					goto out;
927 				*maxcount += nelements;
928 				inp = CAST(cdf_property_info_t *,
929 				    realloc(*info, *maxcount * sizeof(*inp)));
930 				if (inp == NULL)
931 					goto out;
932 				*info = inp;
933 				inp = *info + nelem;
934 			}
935 			DPRINTF(("nelements = %" SIZE_T_FORMAT "u\n",
936 			    nelements));
937 			for (j = 0; j < nelements && i < sh.sh_properties;
938 			    j++, i++)
939 			{
940 				uint32_t l = CDF_GETUINT32(q, o);
941 				inp[i].pi_str.s_len = l;
942 				inp[i].pi_str.s_buf = (const char *)
943 				    (const void *)(&q[o4 + sizeof(l)]);
944 				DPRINTF(("l = %d, r = %" SIZE_T_FORMAT
945 				    "u, s = %s\n", l,
946 				    CDF_ROUND(l, sizeof(l)),
947 				    inp[i].pi_str.s_buf));
948 				if (l & 1)
949 					l++;
950 				o += l >> 1;
951 				if (q + o >= e)
952 					goto out;
953 				o4 = o * sizeof(uint32_t);
954 			}
955 			i--;
956 			break;
957 		case CDF_FILETIME:
958 			if (inp[i].pi_type & CDF_VECTOR)
959 				goto unknown;
960 			(void)memcpy(&tp, &q[o4], sizeof(tp));
961 			inp[i].pi_tp = CDF_TOLE8((uint64_t)tp);
962 			break;
963 		case CDF_CLIPBOARD:
964 			if (inp[i].pi_type & CDF_VECTOR)
965 				goto unknown;
966 			break;
967 		default:
968 		unknown:
969 			DPRINTF(("Don't know how to deal with %x\n",
970 			    inp[i].pi_type));
971 			break;
972 		}
973 	}
974 	return 0;
975 out:
976 	free(*info);
977 	return -1;
978 }
979 
980 int
981 cdf_unpack_summary_info(const cdf_stream_t *sst, const cdf_header_t *h,
982     cdf_summary_info_header_t *ssi, cdf_property_info_t **info, size_t *count)
983 {
984 	size_t maxcount;
985 	const cdf_summary_info_header_t *si =
986 	    CAST(const cdf_summary_info_header_t *, sst->sst_tab);
987 	const cdf_section_declaration_t *sd =
988 	    CAST(const cdf_section_declaration_t *, (const void *)
989 	    ((const char *)sst->sst_tab + CDF_SECTION_DECLARATION_OFFSET));
990 
991 	if (cdf_check_stream_offset(sst, h, si, sizeof(*si), __LINE__) == -1 ||
992 	    cdf_check_stream_offset(sst, h, sd, sizeof(*sd), __LINE__) == -1)
993 		return -1;
994 	ssi->si_byte_order = CDF_TOLE2(si->si_byte_order);
995 	ssi->si_os_version = CDF_TOLE2(si->si_os_version);
996 	ssi->si_os = CDF_TOLE2(si->si_os);
997 	ssi->si_class = si->si_class;
998 	cdf_swap_class(&ssi->si_class);
999 	ssi->si_count = CDF_TOLE4(si->si_count);
1000 	*count = 0;
1001 	maxcount = 0;
1002 	*info = NULL;
1003 	if (cdf_read_property_info(sst, h, CDF_TOLE4(sd->sd_offset), info,
1004 	    count, &maxcount) == -1)
1005 		return -1;
1006 	return 0;
1007 }
1008 
1009 
1010 #define extract_catalog_field(t, f, l) \
1011     memcpy(&ce[i].f, b + (l), sizeof(ce[i].f)); \
1012     ce[i].f = CAST(t, CDF_TOLE(ce[i].f))
1013 
1014 int
1015 cdf_unpack_catalog(const cdf_header_t *h, const cdf_stream_t *sst,
1016     cdf_catalog_t **cat)
1017 {
1018 	size_t ss = sst->sst_dirlen < h->h_min_size_standard_stream ?
1019 	    CDF_SHORT_SEC_SIZE(h) : CDF_SEC_SIZE(h);
1020 	const char *b = CAST(const char *, sst->sst_tab);
1021 	const char *eb = b + ss * sst->sst_len;
1022 	size_t nr, i, k;
1023 	cdf_catalog_entry_t *ce;
1024 	uint16_t reclen;
1025 	const uint16_t *np;
1026 
1027 	for (nr = 0; b < eb; nr++) {
1028 		memcpy(&reclen, b, sizeof(reclen));
1029 		reclen = CDF_TOLE2(reclen);
1030 		if (reclen == 0)
1031 			break;
1032 		b += reclen;
1033 	}
1034 	*cat = CAST(cdf_catalog_t *,
1035 	    malloc(sizeof(cdf_catalog_t) + nr * sizeof(*ce)));
1036 	(*cat)->cat_num = nr;
1037 	ce = (*cat)->cat_e;
1038 	b = CAST(const char *, sst->sst_tab);
1039 	for (i = 0; i < nr; i++) {
1040 		extract_catalog_field(uint16_t, ce_namlen, 0);
1041 		extract_catalog_field(uint16_t, ce_num, 2);
1042 		extract_catalog_field(uint64_t, ce_timestamp, 6);
1043 		reclen = ce[i].ce_namlen;
1044 		ce[i].ce_namlen =
1045 		    sizeof(ce[i].ce_name) / sizeof(ce[i].ce_name[0]) - 1;
1046 		if (ce[i].ce_namlen > reclen - 14)
1047 			ce[i].ce_namlen = reclen - 14;
1048 		np = CAST(const uint16_t *, CAST(const void *, (b + 16)));
1049 		for (k = 0; k < ce[i].ce_namlen; k++) {
1050 			ce[i].ce_name[k] = np[k]; /* XXX: CDF_TOLE2? */
1051 		}
1052 		ce[i].ce_name[ce[i].ce_namlen] = 0;
1053 		b += reclen;
1054 	}
1055 	return 0;
1056 }
1057 
1058 int
1059 cdf_print_classid(char *buf, size_t buflen, const cdf_classid_t *id)
1060 {
1061 	return snprintf(buf, buflen, "%.8x-%.4x-%.4x-%.2x%.2x-"
1062 	    "%.2x%.2x%.2x%.2x%.2x%.2x", id->cl_dword, id->cl_word[0],
1063 	    id->cl_word[1], id->cl_two[0], id->cl_two[1], id->cl_six[0],
1064 	    id->cl_six[1], id->cl_six[2], id->cl_six[3], id->cl_six[4],
1065 	    id->cl_six[5]);
1066 }
1067 
1068 static const struct {
1069 	uint32_t v;
1070 	const char *n;
1071 } vn[] = {
1072 	{ CDF_PROPERTY_CODE_PAGE, "Code page" },
1073 	{ CDF_PROPERTY_TITLE, "Title" },
1074 	{ CDF_PROPERTY_SUBJECT, "Subject" },
1075 	{ CDF_PROPERTY_AUTHOR, "Author" },
1076 	{ CDF_PROPERTY_KEYWORDS, "Keywords" },
1077 	{ CDF_PROPERTY_COMMENTS, "Comments" },
1078 	{ CDF_PROPERTY_TEMPLATE, "Template" },
1079 	{ CDF_PROPERTY_LAST_SAVED_BY, "Last Saved By" },
1080 	{ CDF_PROPERTY_REVISION_NUMBER, "Revision Number" },
1081 	{ CDF_PROPERTY_TOTAL_EDITING_TIME, "Total Editing Time" },
1082 	{ CDF_PROPERTY_LAST_PRINTED, "Last Printed" },
1083 	{ CDF_PROPERTY_CREATE_TIME, "Create Time/Date" },
1084 	{ CDF_PROPERTY_LAST_SAVED_TIME, "Last Saved Time/Date" },
1085 	{ CDF_PROPERTY_NUMBER_OF_PAGES, "Number of Pages" },
1086 	{ CDF_PROPERTY_NUMBER_OF_WORDS, "Number of Words" },
1087 	{ CDF_PROPERTY_NUMBER_OF_CHARACTERS, "Number of Characters" },
1088 	{ CDF_PROPERTY_THUMBNAIL, "Thumbnail" },
1089 	{ CDF_PROPERTY_NAME_OF_APPLICATION, "Name of Creating Application" },
1090 	{ CDF_PROPERTY_SECURITY, "Security" },
1091 	{ CDF_PROPERTY_LOCALE_ID, "Locale ID" },
1092 };
1093 
1094 int
1095 cdf_print_property_name(char *buf, size_t bufsiz, uint32_t p)
1096 {
1097 	size_t i;
1098 
1099 	for (i = 0; i < __arraycount(vn); i++)
1100 		if (vn[i].v == p)
1101 			return snprintf(buf, bufsiz, "%s", vn[i].n);
1102 	return snprintf(buf, bufsiz, "0x%x", p);
1103 }
1104 
1105 int
1106 cdf_print_elapsed_time(char *buf, size_t bufsiz, cdf_timestamp_t ts)
1107 {
1108 	int len = 0;
1109 	int days, hours, mins, secs;
1110 
1111 	ts /= CDF_TIME_PREC;
1112 	secs = (int)(ts % 60);
1113 	ts /= 60;
1114 	mins = (int)(ts % 60);
1115 	ts /= 60;
1116 	hours = (int)(ts % 24);
1117 	ts /= 24;
1118 	days = (int)ts;
1119 
1120 	if (days) {
1121 		len += snprintf(buf + len, bufsiz - len, "%dd+", days);
1122 		if ((size_t)len >= bufsiz)
1123 			return len;
1124 	}
1125 
1126 	if (days || hours) {
1127 		len += snprintf(buf + len, bufsiz - len, "%.2d:", hours);
1128 		if ((size_t)len >= bufsiz)
1129 			return len;
1130 	}
1131 
1132 	len += snprintf(buf + len, bufsiz - len, "%.2d:", mins);
1133 	if ((size_t)len >= bufsiz)
1134 		return len;
1135 
1136 	len += snprintf(buf + len, bufsiz - len, "%.2d", secs);
1137 	return len;
1138 }
1139 
1140 char *
1141 cdf_u16tos8(char *buf, size_t len, const uint16_t *p)
1142 {
1143 	size_t i;
1144 	for (i = 0; i < len && p[i]; i++)
1145 		buf[i] = (char)p[i];
1146 	buf[i] = '\0';
1147 	return buf;
1148 }
1149 
1150 #ifdef CDF_DEBUG
1151 void
1152 cdf_dump_header(const cdf_header_t *h)
1153 {
1154 	size_t i;
1155 
1156 #define DUMP(a, b) (void)fprintf(stderr, "%40.40s = " a "\n", # b, h->h_ ## b)
1157 #define DUMP2(a, b) (void)fprintf(stderr, "%40.40s = " a " (" a ")\n", # b, \
1158     h->h_ ## b, 1 << h->h_ ## b)
1159 	DUMP("%d", revision);
1160 	DUMP("%d", version);
1161 	DUMP("0x%x", byte_order);
1162 	DUMP2("%d", sec_size_p2);
1163 	DUMP2("%d", short_sec_size_p2);
1164 	DUMP("%d", num_sectors_in_sat);
1165 	DUMP("%d", secid_first_directory);
1166 	DUMP("%d", min_size_standard_stream);
1167 	DUMP("%d", secid_first_sector_in_short_sat);
1168 	DUMP("%d", num_sectors_in_short_sat);
1169 	DUMP("%d", secid_first_sector_in_master_sat);
1170 	DUMP("%d", num_sectors_in_master_sat);
1171 	for (i = 0; i < __arraycount(h->h_master_sat); i++) {
1172 		if (h->h_master_sat[i] == CDF_SECID_FREE)
1173 			break;
1174 		(void)fprintf(stderr, "%35.35s[%.3" SIZE_T_FORMAT "u] = %d\n",
1175 		    "master_sat", i, h->h_master_sat[i]);
1176 	}
1177 }
1178 
1179 void
1180 cdf_dump_sat(const char *prefix, const cdf_sat_t *sat, size_t size)
1181 {
1182 	size_t i, j, s = size / sizeof(cdf_secid_t);
1183 
1184 	for (i = 0; i < sat->sat_len; i++) {
1185 		(void)fprintf(stderr, "%s[%" SIZE_T_FORMAT "u]:\n%.6"
1186 		    SIZE_T_FORMAT "u: ", prefix, i, i * s);
1187 		for (j = 0; j < s; j++) {
1188 			(void)fprintf(stderr, "%5d, ",
1189 			    CDF_TOLE4(sat->sat_tab[s * i + j]));
1190 			if ((j + 1) % 10 == 0)
1191 				(void)fprintf(stderr, "\n%.6" SIZE_T_FORMAT
1192 				    "u: ", i * s + j + 1);
1193 		}
1194 		(void)fprintf(stderr, "\n");
1195 	}
1196 }
1197 
1198 void
1199 cdf_dump(void *v, size_t len)
1200 {
1201 	size_t i, j;
1202 	unsigned char *p = v;
1203 	char abuf[16];
1204 	(void)fprintf(stderr, "%.4x: ", 0);
1205 	for (i = 0, j = 0; i < len; i++, p++) {
1206 		(void)fprintf(stderr, "%.2x ", *p);
1207 		abuf[j++] = isprint(*p) ? *p : '.';
1208 		if (j == 16) {
1209 			j = 0;
1210 			abuf[15] = '\0';
1211 			(void)fprintf(stderr, "%s\n%.4" SIZE_T_FORMAT "x: ",
1212 			    abuf, i + 1);
1213 		}
1214 	}
1215 	(void)fprintf(stderr, "\n");
1216 }
1217 
1218 void
1219 cdf_dump_stream(const cdf_header_t *h, const cdf_stream_t *sst)
1220 {
1221 	size_t ss = sst->sst_dirlen < h->h_min_size_standard_stream ?
1222 	    CDF_SHORT_SEC_SIZE(h) : CDF_SEC_SIZE(h);
1223 	cdf_dump(sst->sst_tab, ss * sst->sst_len);
1224 }
1225 
1226 void
1227 cdf_dump_dir(const cdf_info_t *info, const cdf_header_t *h,
1228     const cdf_sat_t *sat, const cdf_sat_t *ssat, const cdf_stream_t *sst,
1229     const cdf_dir_t *dir)
1230 {
1231 	size_t i, j;
1232 	cdf_directory_t *d;
1233 	char name[__arraycount(d->d_name)];
1234 	cdf_stream_t scn;
1235 	struct timespec ts;
1236 
1237 	static const char *types[] = { "empty", "user storage",
1238 	    "user stream", "lockbytes", "property", "root storage" };
1239 
1240 	for (i = 0; i < dir->dir_len; i++) {
1241 		char buf[26];
1242 		d = &dir->dir_tab[i];
1243 		for (j = 0; j < sizeof(name); j++)
1244 			name[j] = (char)CDF_TOLE2(d->d_name[j]);
1245 		(void)fprintf(stderr, "Directory %" SIZE_T_FORMAT "u: %s\n",
1246 		    i, name);
1247 		if (d->d_type < __arraycount(types))
1248 			(void)fprintf(stderr, "Type: %s\n", types[d->d_type]);
1249 		else
1250 			(void)fprintf(stderr, "Type: %d\n", d->d_type);
1251 		(void)fprintf(stderr, "Color: %s\n",
1252 		    d->d_color ? "black" : "red");
1253 		(void)fprintf(stderr, "Left child: %d\n", d->d_left_child);
1254 		(void)fprintf(stderr, "Right child: %d\n", d->d_right_child);
1255 		(void)fprintf(stderr, "Flags: 0x%x\n", d->d_flags);
1256 		cdf_timestamp_to_timespec(&ts, d->d_created);
1257 		(void)fprintf(stderr, "Created %s", cdf_ctime(&ts.tv_sec, buf));
1258 		cdf_timestamp_to_timespec(&ts, d->d_modified);
1259 		(void)fprintf(stderr, "Modified %s",
1260 		    cdf_ctime(&ts.tv_sec, buf));
1261 		(void)fprintf(stderr, "Stream %d\n", d->d_stream_first_sector);
1262 		(void)fprintf(stderr, "Size %d\n", d->d_size);
1263 		switch (d->d_type) {
1264 		case CDF_DIR_TYPE_USER_STORAGE:
1265 			(void)fprintf(stderr, "Storage: %d\n", d->d_storage);
1266 			break;
1267 		case CDF_DIR_TYPE_USER_STREAM:
1268 			if (sst == NULL)
1269 				break;
1270 			if (cdf_read_sector_chain(info, h, sat, ssat, sst,
1271 			    d->d_stream_first_sector, d->d_size, &scn) == -1) {
1272 				warn("Can't read stream for %s at %d len %d",
1273 				    name, d->d_stream_first_sector, d->d_size);
1274 				break;
1275 			}
1276 			cdf_dump_stream(h, &scn);
1277 			free(scn.sst_tab);
1278 			break;
1279 		default:
1280 			break;
1281 		}
1282 
1283 	}
1284 }
1285 
1286 void
1287 cdf_dump_property_info(const cdf_property_info_t *info, size_t count)
1288 {
1289 	cdf_timestamp_t tp;
1290 	struct timespec ts;
1291 	char buf[64];
1292 	size_t i, j;
1293 
1294 	for (i = 0; i < count; i++) {
1295 		cdf_print_property_name(buf, sizeof(buf), info[i].pi_id);
1296 		(void)fprintf(stderr, "%" SIZE_T_FORMAT "u) %s: ", i, buf);
1297 		switch (info[i].pi_type) {
1298 		case CDF_NULL:
1299 			break;
1300 		case CDF_SIGNED16:
1301 			(void)fprintf(stderr, "signed 16 [%hd]\n",
1302 			    info[i].pi_s16);
1303 			break;
1304 		case CDF_SIGNED32:
1305 			(void)fprintf(stderr, "signed 32 [%d]\n",
1306 			    info[i].pi_s32);
1307 			break;
1308 		case CDF_UNSIGNED32:
1309 			(void)fprintf(stderr, "unsigned 32 [%u]\n",
1310 			    info[i].pi_u32);
1311 			break;
1312 		case CDF_FLOAT:
1313 			(void)fprintf(stderr, "float [%g]\n",
1314 			    info[i].pi_f);
1315 			break;
1316 		case CDF_DOUBLE:
1317 			(void)fprintf(stderr, "double [%g]\n",
1318 			    info[i].pi_d);
1319 			break;
1320 		case CDF_LENGTH32_STRING:
1321 			(void)fprintf(stderr, "string %u [%.*s]\n",
1322 			    info[i].pi_str.s_len,
1323 			    info[i].pi_str.s_len, info[i].pi_str.s_buf);
1324 			break;
1325 		case CDF_LENGTH32_WSTRING:
1326 			(void)fprintf(stderr, "string %u [",
1327 			    info[i].pi_str.s_len);
1328 			for (j = 0; j < info[i].pi_str.s_len - 1; j++)
1329 			    (void)fputc(info[i].pi_str.s_buf[j << 1], stderr);
1330 			(void)fprintf(stderr, "]\n");
1331 			break;
1332 		case CDF_FILETIME:
1333 			tp = info[i].pi_tp;
1334 			if (tp < 1000000000000000LL) {
1335 				cdf_print_elapsed_time(buf, sizeof(buf), tp);
1336 				(void)fprintf(stderr, "timestamp %s\n", buf);
1337 			} else {
1338 				char buf[26];
1339 				cdf_timestamp_to_timespec(&ts, tp);
1340 				(void)fprintf(stderr, "timestamp %s",
1341 				    cdf_ctime(&ts.tv_sec, buf));
1342 			}
1343 			break;
1344 		case CDF_CLIPBOARD:
1345 			(void)fprintf(stderr, "CLIPBOARD %u\n", info[i].pi_u32);
1346 			break;
1347 		default:
1348 			DPRINTF(("Don't know how to deal with %x\n",
1349 			    info[i].pi_type));
1350 			break;
1351 		}
1352 	}
1353 }
1354 
1355 
1356 void
1357 cdf_dump_summary_info(const cdf_header_t *h, const cdf_stream_t *sst)
1358 {
1359 	char buf[128];
1360 	cdf_summary_info_header_t ssi;
1361 	cdf_property_info_t *info;
1362 	size_t count;
1363 
1364 	(void)&h;
1365 	if (cdf_unpack_summary_info(sst, h, &ssi, &info, &count) == -1)
1366 		return;
1367 	(void)fprintf(stderr, "Endian: %x\n", ssi.si_byte_order);
1368 	(void)fprintf(stderr, "Os Version %d.%d\n", ssi.si_os_version & 0xff,
1369 	    ssi.si_os_version >> 8);
1370 	(void)fprintf(stderr, "Os %d\n", ssi.si_os);
1371 	cdf_print_classid(buf, sizeof(buf), &ssi.si_class);
1372 	(void)fprintf(stderr, "Class %s\n", buf);
1373 	(void)fprintf(stderr, "Count %d\n", ssi.si_count);
1374 	cdf_dump_property_info(info, count);
1375 	free(info);
1376 }
1377 
1378 
1379 void
1380 cdf_dump_catalog(const cdf_header_t *h, const cdf_stream_t *sst)
1381 {
1382 	cdf_catalog_t *cat;
1383 	cdf_unpack_catalog(h, sst, &cat);
1384 	const cdf_catalog_entry_t *ce = cat->cat_e;
1385 	struct timespec ts;
1386 	char tbuf[64], sbuf[256];
1387 	size_t i;
1388 
1389 	printf("Catalog:\n");
1390 	for (i = 0; i < cat->cat_num; i++) {
1391 		cdf_timestamp_to_timespec(&ts, ce[i].ce_timestamp);
1392 		printf("\t%d %s %s", ce[i].ce_num,
1393 		    cdf_u16tos8(sbuf, ce[i].ce_namlen, ce[i].ce_name),
1394 		    cdf_ctime(&ts.tv_sec, tbuf));
1395 	}
1396 	free(cat);
1397 }
1398 
1399 #endif
1400 
1401 #ifdef TEST
1402 int
1403 main(int argc, char *argv[])
1404 {
1405 	int i;
1406 	cdf_header_t h;
1407 	cdf_sat_t sat, ssat;
1408 	cdf_stream_t sst, scn;
1409 	cdf_dir_t dir;
1410 	cdf_info_t info;
1411 	const cdf_directory_t *root;
1412 
1413 	if (argc < 2) {
1414 		(void)fprintf(stderr, "Usage: %s <filename>\n", getprogname());
1415 		return -1;
1416 	}
1417 
1418 	info.i_buf = NULL;
1419 	info.i_len = 0;
1420 	for (i = 1; i < argc; i++) {
1421 		if ((info.i_fd = open(argv[1], O_RDONLY)) == -1)
1422 			err(1, "Cannot open `%s'", argv[1]);
1423 
1424 		if (cdf_read_header(&info, &h) == -1)
1425 			err(1, "Cannot read header");
1426 #ifdef CDF_DEBUG
1427 		cdf_dump_header(&h);
1428 #endif
1429 
1430 		if (cdf_read_sat(&info, &h, &sat) == -1)
1431 			err(1, "Cannot read sat");
1432 #ifdef CDF_DEBUG
1433 		cdf_dump_sat("SAT", &sat, CDF_SEC_SIZE(&h));
1434 #endif
1435 
1436 		if (cdf_read_ssat(&info, &h, &sat, &ssat) == -1)
1437 			err(1, "Cannot read ssat");
1438 #ifdef CDF_DEBUG
1439 		cdf_dump_sat("SSAT", &ssat, CDF_SHORT_SEC_SIZE(&h));
1440 #endif
1441 
1442 		if (cdf_read_dir(&info, &h, &sat, &dir) == -1)
1443 			err(1, "Cannot read dir");
1444 
1445 		if (cdf_read_short_stream(&info, &h, &sat, &dir, &sst, &root)
1446 		    == -1)
1447 			err(1, "Cannot read short stream");
1448 #ifdef CDF_DEBUG
1449 		cdf_dump_stream(&h, &sst);
1450 #endif
1451 
1452 #ifdef CDF_DEBUG
1453 		cdf_dump_dir(&info, &h, &sat, &ssat, &sst, &dir);
1454 #endif
1455 
1456 
1457 		if (cdf_read_summary_info(&info, &h, &sat, &ssat, &sst, &dir,
1458 		    &scn) == -1)
1459 			warn("Cannot read summary info");
1460 #ifdef CDF_DEBUG
1461 		else
1462 			cdf_dump_summary_info(&h, &scn);
1463 #endif
1464 		if (cdf_read_catalog(&info, &h, &sat, &ssat, &sst, &dir,
1465 		    &scn) == -1)
1466 			warn("Cannot read catalog");
1467 #ifdef CDF_DEBUG
1468 		else
1469 			cdf_dump_catalog(&h, &scn);
1470 #endif
1471 
1472 		(void)close(info.i_fd);
1473 	}
1474 
1475 	return 0;
1476 }
1477 #endif
1478