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