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