cdf.c revision 284778
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.76 2015/02/28 00:18:02 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_check_stream_offset(const cdf_stream_t *sst, const cdf_header_t *h,
271    const void *p, size_t tail, int line)
272{
273	const char *b = (const char *)sst->sst_tab;
274	const char *e = ((const char *)p) + tail;
275	size_t ss = sst->sst_dirlen < h->h_min_size_standard_stream ?
276	    CDF_SHORT_SEC_SIZE(h) : CDF_SEC_SIZE(h);
277	/*LINTED*/(void)&line;
278	if (e >= b && (size_t)(e - b) <= ss * sst->sst_len)
279		return 0;
280	DPRINTF(("%d: offset begin %p < end %p || %" SIZE_T_FORMAT "u"
281	    " > %" SIZE_T_FORMAT "u [%" SIZE_T_FORMAT "u %"
282	    SIZE_T_FORMAT "u]\n", line, b, e, (size_t)(e - b),
283	    ss * sst->sst_len, ss, sst->sst_len));
284	errno = EFTYPE;
285	return -1;
286}
287
288static ssize_t
289cdf_read(const cdf_info_t *info, off_t off, void *buf, size_t len)
290{
291	size_t siz = (size_t)off + len;
292
293	if ((off_t)(off + len) != (off_t)siz) {
294		errno = EINVAL;
295		return -1;
296	}
297
298	if (info->i_buf != NULL && info->i_len >= siz) {
299		(void)memcpy(buf, &info->i_buf[off], len);
300		return (ssize_t)len;
301	}
302
303	if (info->i_fd == -1)
304		return -1;
305
306	if (pread(info->i_fd, buf, len, off) != (ssize_t)len)
307		return -1;
308
309	return (ssize_t)len;
310}
311
312int
313cdf_read_header(const cdf_info_t *info, cdf_header_t *h)
314{
315	char buf[512];
316
317	(void)memcpy(cdf_bo.s, "\01\02\03\04", 4);
318	if (cdf_read(info, (off_t)0, buf, sizeof(buf)) == -1)
319		return -1;
320	cdf_unpack_header(h, buf);
321	cdf_swap_header(h);
322	if (h->h_magic != CDF_MAGIC) {
323		DPRINTF(("Bad magic 0x%" INT64_T_FORMAT "x != 0x%"
324		    INT64_T_FORMAT "x\n",
325		    (unsigned long long)h->h_magic,
326		    (unsigned long long)CDF_MAGIC));
327		goto out;
328	}
329	if (h->h_sec_size_p2 > 20) {
330		DPRINTF(("Bad sector size 0x%u\n", h->h_sec_size_p2));
331		goto out;
332	}
333	if (h->h_short_sec_size_p2 > 20) {
334		DPRINTF(("Bad short sector size 0x%u\n",
335		    h->h_short_sec_size_p2));
336		goto out;
337	}
338	return 0;
339out:
340	errno = EFTYPE;
341	return -1;
342}
343
344
345ssize_t
346cdf_read_sector(const cdf_info_t *info, void *buf, size_t offs, size_t len,
347    const cdf_header_t *h, cdf_secid_t id)
348{
349	size_t ss = CDF_SEC_SIZE(h);
350	size_t pos = CDF_SEC_POS(h, id);
351	assert(ss == len);
352	return cdf_read(info, (off_t)pos, ((char *)buf) + offs, len);
353}
354
355ssize_t
356cdf_read_short_sector(const cdf_stream_t *sst, void *buf, size_t offs,
357    size_t len, const cdf_header_t *h, cdf_secid_t id)
358{
359	size_t ss = CDF_SHORT_SEC_SIZE(h);
360	size_t pos = CDF_SHORT_SEC_POS(h, id);
361	assert(ss == len);
362	if (pos + len > CDF_SEC_SIZE(h) * sst->sst_len) {
363		DPRINTF(("Out of bounds read %" SIZE_T_FORMAT "u > %"
364		    SIZE_T_FORMAT "u\n",
365		    pos + len, CDF_SEC_SIZE(h) * sst->sst_len));
366		return -1;
367	}
368	(void)memcpy(((char *)buf) + offs,
369	    ((const char *)sst->sst_tab) + pos, len);
370	return len;
371}
372
373/*
374 * Read the sector allocation table.
375 */
376int
377cdf_read_sat(const cdf_info_t *info, cdf_header_t *h, cdf_sat_t *sat)
378{
379	size_t i, j, k;
380	size_t ss = CDF_SEC_SIZE(h);
381	cdf_secid_t *msa, mid, sec;
382	size_t nsatpersec = (ss / sizeof(mid)) - 1;
383
384	for (i = 0; i < __arraycount(h->h_master_sat); i++)
385		if (h->h_master_sat[i] == CDF_SECID_FREE)
386			break;
387
388#define CDF_SEC_LIMIT (UINT32_MAX / (4 * ss))
389	if ((nsatpersec > 0 &&
390	    h->h_num_sectors_in_master_sat > CDF_SEC_LIMIT / nsatpersec) ||
391	    i > CDF_SEC_LIMIT) {
392		DPRINTF(("Number of sectors in master SAT too big %u %"
393		    SIZE_T_FORMAT "u\n", h->h_num_sectors_in_master_sat, i));
394		errno = EFTYPE;
395		return -1;
396	}
397
398	sat->sat_len = h->h_num_sectors_in_master_sat * nsatpersec + i;
399	DPRINTF(("sat_len = %" SIZE_T_FORMAT "u ss = %" SIZE_T_FORMAT "u\n",
400	    sat->sat_len, ss));
401	if ((sat->sat_tab = CAST(cdf_secid_t *, calloc(sat->sat_len, ss)))
402	    == NULL)
403		return -1;
404
405	for (i = 0; i < __arraycount(h->h_master_sat); i++) {
406		if (h->h_master_sat[i] < 0)
407			break;
408		if (cdf_read_sector(info, sat->sat_tab, ss * i, ss, h,
409		    h->h_master_sat[i]) != (ssize_t)ss) {
410			DPRINTF(("Reading sector %d", h->h_master_sat[i]));
411			goto out1;
412		}
413	}
414
415	if ((msa = CAST(cdf_secid_t *, calloc(1, ss))) == NULL)
416		goto out1;
417
418	mid = h->h_secid_first_sector_in_master_sat;
419	for (j = 0; j < h->h_num_sectors_in_master_sat; j++) {
420		if (mid < 0)
421			goto out;
422		if (j >= CDF_LOOP_LIMIT) {
423			DPRINTF(("Reading master sector loop limit"));
424			errno = EFTYPE;
425			goto out2;
426		}
427		if (cdf_read_sector(info, msa, 0, ss, h, mid) != (ssize_t)ss) {
428			DPRINTF(("Reading master sector %d", mid));
429			goto out2;
430		}
431		for (k = 0; k < nsatpersec; k++, i++) {
432			sec = CDF_TOLE4((uint32_t)msa[k]);
433			if (sec < 0)
434				goto out;
435			if (i >= sat->sat_len) {
436			    DPRINTF(("Out of bounds reading MSA %" SIZE_T_FORMAT
437				"u >= %" SIZE_T_FORMAT "u", i, sat->sat_len));
438			    errno = EFTYPE;
439			    goto out2;
440			}
441			if (cdf_read_sector(info, sat->sat_tab, ss * i, ss, h,
442			    sec) != (ssize_t)ss) {
443				DPRINTF(("Reading sector %d",
444				    CDF_TOLE4(msa[k])));
445				goto out2;
446			}
447		}
448		mid = CDF_TOLE4((uint32_t)msa[nsatpersec]);
449	}
450out:
451	sat->sat_len = i;
452	free(msa);
453	return 0;
454out2:
455	free(msa);
456out1:
457	free(sat->sat_tab);
458	return -1;
459}
460
461size_t
462cdf_count_chain(const cdf_sat_t *sat, cdf_secid_t sid, size_t size)
463{
464	size_t i, j;
465	cdf_secid_t maxsector = (cdf_secid_t)((sat->sat_len * size)
466	    / sizeof(maxsector));
467
468	DPRINTF(("Chain:"));
469	if (sid == CDF_SECID_END_OF_CHAIN) {
470		/* 0-length chain. */
471		DPRINTF((" empty\n"));
472		return 0;
473	}
474
475	for (j = i = 0; sid >= 0; i++, j++) {
476		DPRINTF((" %d", sid));
477		if (j >= CDF_LOOP_LIMIT) {
478			DPRINTF(("Counting chain loop limit"));
479			errno = EFTYPE;
480			return (size_t)-1;
481		}
482		if (sid >= maxsector) {
483			DPRINTF(("Sector %d >= %d\n", sid, maxsector));
484			errno = EFTYPE;
485			return (size_t)-1;
486		}
487		sid = CDF_TOLE4((uint32_t)sat->sat_tab[sid]);
488	}
489	if (i == 0) {
490		DPRINTF((" none, sid: %d\n", sid));
491		return (size_t)-1;
492
493	}
494	DPRINTF(("\n"));
495	return i;
496}
497
498int
499cdf_read_long_sector_chain(const cdf_info_t *info, const cdf_header_t *h,
500    const cdf_sat_t *sat, cdf_secid_t sid, size_t len, cdf_stream_t *scn)
501{
502	size_t ss = CDF_SEC_SIZE(h), i, j;
503	ssize_t nr;
504	scn->sst_len = cdf_count_chain(sat, sid, ss);
505	scn->sst_dirlen = len;
506
507	if (scn->sst_len == (size_t)-1)
508		return -1;
509
510	scn->sst_tab = calloc(scn->sst_len, ss);
511	if (scn->sst_tab == NULL)
512		return -1;
513
514	for (j = i = 0; sid >= 0; i++, j++) {
515		if (j >= CDF_LOOP_LIMIT) {
516			DPRINTF(("Read long sector chain loop limit"));
517			errno = EFTYPE;
518			goto out;
519		}
520		if (i >= scn->sst_len) {
521			DPRINTF(("Out of bounds reading long sector chain "
522			    "%" SIZE_T_FORMAT "u > %" SIZE_T_FORMAT "u\n", i,
523			    scn->sst_len));
524			errno = EFTYPE;
525			goto out;
526		}
527		if ((nr = cdf_read_sector(info, scn->sst_tab, i * ss, ss, h,
528		    sid)) != (ssize_t)ss) {
529			if (i == scn->sst_len - 1 && nr > 0) {
530				/* Last sector might be truncated */
531				return 0;
532			}
533			DPRINTF(("Reading long sector chain %d", sid));
534			goto out;
535		}
536		sid = CDF_TOLE4((uint32_t)sat->sat_tab[sid]);
537	}
538	return 0;
539out:
540	free(scn->sst_tab);
541	return -1;
542}
543
544int
545cdf_read_short_sector_chain(const cdf_header_t *h,
546    const cdf_sat_t *ssat, const cdf_stream_t *sst,
547    cdf_secid_t sid, size_t len, cdf_stream_t *scn)
548{
549	size_t ss = CDF_SHORT_SEC_SIZE(h), i, j;
550	scn->sst_len = cdf_count_chain(ssat, sid, CDF_SEC_SIZE(h));
551	scn->sst_dirlen = len;
552
553	if (sst->sst_tab == NULL || scn->sst_len == (size_t)-1)
554		return -1;
555
556	scn->sst_tab = calloc(scn->sst_len, ss);
557	if (scn->sst_tab == NULL)
558		return -1;
559
560	for (j = i = 0; sid >= 0; i++, j++) {
561		if (j >= CDF_LOOP_LIMIT) {
562			DPRINTF(("Read short sector chain loop limit"));
563			errno = EFTYPE;
564			goto out;
565		}
566		if (i >= scn->sst_len) {
567			DPRINTF(("Out of bounds reading short sector chain "
568			    "%" SIZE_T_FORMAT "u > %" SIZE_T_FORMAT "u\n",
569			    i, scn->sst_len));
570			errno = EFTYPE;
571			goto out;
572		}
573		if (cdf_read_short_sector(sst, scn->sst_tab, i * ss, ss, h,
574		    sid) != (ssize_t)ss) {
575			DPRINTF(("Reading short sector chain %d", sid));
576			goto out;
577		}
578		sid = CDF_TOLE4((uint32_t)ssat->sat_tab[sid]);
579	}
580	return 0;
581out:
582	free(scn->sst_tab);
583	return -1;
584}
585
586int
587cdf_read_sector_chain(const cdf_info_t *info, const cdf_header_t *h,
588    const cdf_sat_t *sat, const cdf_sat_t *ssat, const cdf_stream_t *sst,
589    cdf_secid_t sid, size_t len, cdf_stream_t *scn)
590{
591
592	if (len < h->h_min_size_standard_stream && sst->sst_tab != NULL)
593		return cdf_read_short_sector_chain(h, ssat, sst, sid, len,
594		    scn);
595	else
596		return cdf_read_long_sector_chain(info, h, sat, sid, len, scn);
597}
598
599int
600cdf_read_dir(const cdf_info_t *info, const cdf_header_t *h,
601    const cdf_sat_t *sat, cdf_dir_t *dir)
602{
603	size_t i, j;
604	size_t ss = CDF_SEC_SIZE(h), ns, nd;
605	char *buf;
606	cdf_secid_t sid = h->h_secid_first_directory;
607
608	ns = cdf_count_chain(sat, sid, ss);
609	if (ns == (size_t)-1)
610		return -1;
611
612	nd = ss / CDF_DIRECTORY_SIZE;
613
614	dir->dir_len = ns * nd;
615	dir->dir_tab = CAST(cdf_directory_t *,
616	    calloc(dir->dir_len, sizeof(dir->dir_tab[0])));
617	if (dir->dir_tab == NULL)
618		return -1;
619
620	if ((buf = CAST(char *, malloc(ss))) == NULL) {
621		free(dir->dir_tab);
622		return -1;
623	}
624
625	for (j = i = 0; i < ns; i++, j++) {
626		if (j >= CDF_LOOP_LIMIT) {
627			DPRINTF(("Read dir loop limit"));
628			errno = EFTYPE;
629			goto out;
630		}
631		if (cdf_read_sector(info, buf, 0, ss, h, sid) != (ssize_t)ss) {
632			DPRINTF(("Reading directory sector %d", sid));
633			goto out;
634		}
635		for (j = 0; j < nd; j++) {
636			cdf_unpack_dir(&dir->dir_tab[i * nd + j],
637			    &buf[j * CDF_DIRECTORY_SIZE]);
638		}
639		sid = CDF_TOLE4((uint32_t)sat->sat_tab[sid]);
640	}
641	if (NEED_SWAP)
642		for (i = 0; i < dir->dir_len; i++)
643			cdf_swap_dir(&dir->dir_tab[i]);
644	free(buf);
645	return 0;
646out:
647	free(dir->dir_tab);
648	free(buf);
649	return -1;
650}
651
652
653int
654cdf_read_ssat(const cdf_info_t *info, const cdf_header_t *h,
655    const cdf_sat_t *sat, cdf_sat_t *ssat)
656{
657	size_t i, j;
658	size_t ss = CDF_SEC_SIZE(h);
659	cdf_secid_t sid = h->h_secid_first_sector_in_short_sat;
660
661	ssat->sat_len = cdf_count_chain(sat, sid, CDF_SEC_SIZE(h));
662	if (ssat->sat_len == (size_t)-1)
663		return -1;
664
665	ssat->sat_tab = CAST(cdf_secid_t *, calloc(ssat->sat_len, ss));
666	if (ssat->sat_tab == NULL)
667		return -1;
668
669	for (j = i = 0; sid >= 0; i++, j++) {
670		if (j >= CDF_LOOP_LIMIT) {
671			DPRINTF(("Read short sat sector loop limit"));
672			errno = EFTYPE;
673			goto out;
674		}
675		if (i >= ssat->sat_len) {
676			DPRINTF(("Out of bounds reading short sector chain "
677			    "%" SIZE_T_FORMAT "u > %" SIZE_T_FORMAT "u\n", i,
678			    ssat->sat_len));
679			errno = EFTYPE;
680			goto out;
681		}
682		if (cdf_read_sector(info, ssat->sat_tab, i * ss, ss, h, sid) !=
683		    (ssize_t)ss) {
684			DPRINTF(("Reading short sat sector %d", sid));
685			goto out;
686		}
687		sid = CDF_TOLE4((uint32_t)sat->sat_tab[sid]);
688	}
689	return 0;
690out:
691	free(ssat->sat_tab);
692	return -1;
693}
694
695int
696cdf_read_short_stream(const cdf_info_t *info, const cdf_header_t *h,
697    const cdf_sat_t *sat, const cdf_dir_t *dir, cdf_stream_t *scn,
698    const cdf_directory_t **root)
699{
700	size_t i;
701	const cdf_directory_t *d;
702
703	*root = NULL;
704	for (i = 0; i < dir->dir_len; i++)
705		if (dir->dir_tab[i].d_type == CDF_DIR_TYPE_ROOT_STORAGE)
706			break;
707
708	/* If the it is not there, just fake it; some docs don't have it */
709	if (i == dir->dir_len)
710		goto out;
711	d = &dir->dir_tab[i];
712	*root = d;
713
714	/* If the it is not there, just fake it; some docs don't have it */
715	if (d->d_stream_first_sector < 0)
716		goto out;
717
718	return	cdf_read_long_sector_chain(info, h, sat,
719	    d->d_stream_first_sector, d->d_size, scn);
720out:
721	scn->sst_tab = NULL;
722	scn->sst_len = 0;
723	scn->sst_dirlen = 0;
724	return 0;
725}
726
727static int
728cdf_namecmp(const char *d, const uint16_t *s, size_t l)
729{
730	for (; l--; d++, s++)
731		if (*d != CDF_TOLE2(*s))
732			return (unsigned char)*d - CDF_TOLE2(*s);
733	return 0;
734}
735
736int
737cdf_read_summary_info(const cdf_info_t *info, const cdf_header_t *h,
738    const cdf_sat_t *sat, const cdf_sat_t *ssat, const cdf_stream_t *sst,
739    const cdf_dir_t *dir, cdf_stream_t *scn)
740{
741	return cdf_read_user_stream(info, h, sat, ssat, sst, dir,
742	    "\05SummaryInformation", scn);
743}
744
745int
746cdf_read_user_stream(const cdf_info_t *info, const cdf_header_t *h,
747    const cdf_sat_t *sat, const cdf_sat_t *ssat, const cdf_stream_t *sst,
748    const cdf_dir_t *dir, const char *name, cdf_stream_t *scn)
749{
750	const cdf_directory_t *d;
751	int i = cdf_find_stream(dir, name, CDF_DIR_TYPE_USER_STREAM);
752
753	if (i <= 0)
754		return -1;
755
756	d = &dir->dir_tab[i - 1];
757	return cdf_read_sector_chain(info, h, sat, ssat, sst,
758	    d->d_stream_first_sector, d->d_size, scn);
759}
760
761int
762cdf_find_stream(const cdf_dir_t *dir, const char *name, int type)
763{
764	size_t i, name_len = strlen(name) + 1;
765
766	for (i = dir->dir_len; i > 0; i--)
767		if (dir->dir_tab[i - 1].d_type == type &&
768		    cdf_namecmp(name, dir->dir_tab[i - 1].d_name, name_len)
769		    == 0)
770			break;
771	if (i > 0)
772		return i;
773
774	DPRINTF(("Cannot find type %d `%s'\n", type, name));
775	errno = ESRCH;
776	return 0;
777}
778
779int
780cdf_read_property_info(const cdf_stream_t *sst, const cdf_header_t *h,
781    uint32_t offs, cdf_property_info_t **info, size_t *count, size_t *maxcount)
782{
783	const cdf_section_header_t *shp;
784	cdf_section_header_t sh;
785	const uint8_t *p, *q, *e;
786	int16_t s16;
787	int32_t s32;
788	uint32_t u32;
789	int64_t s64;
790	uint64_t u64;
791	cdf_timestamp_t tp;
792	size_t i, o, o4, nelements, j;
793	cdf_property_info_t *inp;
794
795	if (offs > UINT32_MAX / 4) {
796		errno = EFTYPE;
797		goto out;
798	}
799	shp = CAST(const cdf_section_header_t *, (const void *)
800	    ((const char *)sst->sst_tab + offs));
801	if (cdf_check_stream_offset(sst, h, shp, sizeof(*shp), __LINE__) == -1)
802		goto out;
803	sh.sh_len = CDF_TOLE4(shp->sh_len);
804#define CDF_SHLEN_LIMIT (UINT32_MAX / 8)
805	if (sh.sh_len > CDF_SHLEN_LIMIT) {
806		errno = EFTYPE;
807		goto out;
808	}
809	sh.sh_properties = CDF_TOLE4(shp->sh_properties);
810#define CDF_PROP_LIMIT (UINT32_MAX / (4 * sizeof(*inp)))
811	if (sh.sh_properties > CDF_PROP_LIMIT)
812		goto out;
813	DPRINTF(("section len: %u properties %u\n", sh.sh_len,
814	    sh.sh_properties));
815	if (*maxcount) {
816		if (*maxcount > CDF_PROP_LIMIT)
817			goto out;
818		*maxcount += sh.sh_properties;
819		inp = CAST(cdf_property_info_t *,
820		    realloc(*info, *maxcount * sizeof(*inp)));
821	} else {
822		*maxcount = sh.sh_properties;
823		inp = CAST(cdf_property_info_t *,
824		    malloc(*maxcount * sizeof(*inp)));
825	}
826	if (inp == NULL)
827		goto out;
828	*info = inp;
829	inp += *count;
830	*count += sh.sh_properties;
831	p = CAST(const uint8_t *, (const void *)
832	    ((const char *)(const void *)sst->sst_tab +
833	    offs + sizeof(sh)));
834	e = CAST(const uint8_t *, (const void *)
835	    (((const char *)(const void *)shp) + sh.sh_len));
836	if (cdf_check_stream_offset(sst, h, e, 0, __LINE__) == -1)
837		goto out;
838	for (i = 0; i < sh.sh_properties; i++) {
839		size_t tail = (i << 1) + 1;
840		size_t ofs;
841		if (cdf_check_stream_offset(sst, h, p, tail * sizeof(uint32_t),
842		    __LINE__) == -1)
843			goto out;
844		ofs = CDF_GETUINT32(p, tail);
845		q = (const uint8_t *)(const void *)
846		    ((const char *)(const void *)p + ofs
847		    - 2 * sizeof(uint32_t));
848		if (q < p) {
849			DPRINTF(("Wrapped around %p < %p\n", q, p));
850			goto out;
851		}
852		if (q > e) {
853			DPRINTF(("Ran of the end %p > %p\n", q, e));
854			goto out;
855		}
856		inp[i].pi_id = CDF_GETUINT32(p, i << 1);
857		inp[i].pi_type = CDF_GETUINT32(q, 0);
858		DPRINTF(("%" SIZE_T_FORMAT "u) id=%x type=%x offs=0x%tx,0x%x\n",
859		    i, inp[i].pi_id, inp[i].pi_type, q - p, offs));
860		if (inp[i].pi_type & CDF_VECTOR) {
861			nelements = CDF_GETUINT32(q, 1);
862			if (nelements == 0) {
863				DPRINTF(("CDF_VECTOR with nelements == 0\n"));
864				goto out;
865			}
866			o = 2;
867		} else {
868			nelements = 1;
869			o = 1;
870		}
871		o4 = o * sizeof(uint32_t);
872		if (inp[i].pi_type & (CDF_ARRAY|CDF_BYREF|CDF_RESERVED))
873			goto unknown;
874		switch (inp[i].pi_type & CDF_TYPEMASK) {
875		case CDF_NULL:
876		case CDF_EMPTY:
877			break;
878		case CDF_SIGNED16:
879			if (inp[i].pi_type & CDF_VECTOR)
880				goto unknown;
881			(void)memcpy(&s16, &q[o4], sizeof(s16));
882			inp[i].pi_s16 = CDF_TOLE2(s16);
883			break;
884		case CDF_SIGNED32:
885			if (inp[i].pi_type & CDF_VECTOR)
886				goto unknown;
887			(void)memcpy(&s32, &q[o4], sizeof(s32));
888			inp[i].pi_s32 = CDF_TOLE4((uint32_t)s32);
889			break;
890		case CDF_BOOL:
891		case CDF_UNSIGNED32:
892			if (inp[i].pi_type & CDF_VECTOR)
893				goto unknown;
894			(void)memcpy(&u32, &q[o4], sizeof(u32));
895			inp[i].pi_u32 = CDF_TOLE4(u32);
896			break;
897		case CDF_SIGNED64:
898			if (inp[i].pi_type & CDF_VECTOR)
899				goto unknown;
900			(void)memcpy(&s64, &q[o4], sizeof(s64));
901			inp[i].pi_s64 = CDF_TOLE8((uint64_t)s64);
902			break;
903		case CDF_UNSIGNED64:
904			if (inp[i].pi_type & CDF_VECTOR)
905				goto unknown;
906			(void)memcpy(&u64, &q[o4], sizeof(u64));
907			inp[i].pi_u64 = CDF_TOLE8((uint64_t)u64);
908			break;
909		case CDF_FLOAT:
910			if (inp[i].pi_type & CDF_VECTOR)
911				goto unknown;
912			(void)memcpy(&u32, &q[o4], sizeof(u32));
913			u32 = CDF_TOLE4(u32);
914			memcpy(&inp[i].pi_f, &u32, sizeof(inp[i].pi_f));
915			break;
916		case CDF_DOUBLE:
917			if (inp[i].pi_type & CDF_VECTOR)
918				goto unknown;
919			(void)memcpy(&u64, &q[o4], sizeof(u64));
920			u64 = CDF_TOLE8((uint64_t)u64);
921			memcpy(&inp[i].pi_d, &u64, sizeof(inp[i].pi_d));
922			break;
923		case CDF_LENGTH32_STRING:
924		case CDF_LENGTH32_WSTRING:
925			if (nelements > 1) {
926				size_t nelem = inp - *info;
927				if (*maxcount > CDF_PROP_LIMIT
928				    || nelements > CDF_PROP_LIMIT)
929					goto out;
930				*maxcount += nelements;
931				inp = CAST(cdf_property_info_t *,
932				    realloc(*info, *maxcount * sizeof(*inp)));
933				if (inp == NULL)
934					goto out;
935				*info = inp;
936				inp = *info + nelem;
937			}
938			DPRINTF(("nelements = %" SIZE_T_FORMAT "u\n",
939			    nelements));
940			for (j = 0; j < nelements && i < sh.sh_properties;
941			    j++, i++)
942			{
943				uint32_t l = CDF_GETUINT32(q, o);
944				inp[i].pi_str.s_len = l;
945				inp[i].pi_str.s_buf = (const char *)
946				    (const void *)(&q[o4 + sizeof(l)]);
947				DPRINTF(("l = %d, r = %" SIZE_T_FORMAT
948				    "u, s = %s\n", l,
949				    CDF_ROUND(l, sizeof(l)),
950				    inp[i].pi_str.s_buf));
951				if (l & 1)
952					l++;
953				o += l >> 1;
954				if (q + o >= e)
955					goto out;
956				o4 = o * sizeof(uint32_t);
957			}
958			i--;
959			break;
960		case CDF_FILETIME:
961			if (inp[i].pi_type & CDF_VECTOR)
962				goto unknown;
963			(void)memcpy(&tp, &q[o4], sizeof(tp));
964			inp[i].pi_tp = CDF_TOLE8((uint64_t)tp);
965			break;
966		case CDF_CLIPBOARD:
967			if (inp[i].pi_type & CDF_VECTOR)
968				goto unknown;
969			break;
970		default:
971		unknown:
972			DPRINTF(("Don't know how to deal with %x\n",
973			    inp[i].pi_type));
974			break;
975		}
976	}
977	return 0;
978out:
979	free(*info);
980	return -1;
981}
982
983int
984cdf_unpack_summary_info(const cdf_stream_t *sst, const cdf_header_t *h,
985    cdf_summary_info_header_t *ssi, cdf_property_info_t **info, size_t *count)
986{
987	size_t maxcount;
988	const cdf_summary_info_header_t *si =
989	    CAST(const cdf_summary_info_header_t *, sst->sst_tab);
990	const cdf_section_declaration_t *sd =
991	    CAST(const cdf_section_declaration_t *, (const void *)
992	    ((const char *)sst->sst_tab + CDF_SECTION_DECLARATION_OFFSET));
993
994	if (cdf_check_stream_offset(sst, h, si, sizeof(*si), __LINE__) == -1 ||
995	    cdf_check_stream_offset(sst, h, sd, sizeof(*sd), __LINE__) == -1)
996		return -1;
997	ssi->si_byte_order = CDF_TOLE2(si->si_byte_order);
998	ssi->si_os_version = CDF_TOLE2(si->si_os_version);
999	ssi->si_os = CDF_TOLE2(si->si_os);
1000	ssi->si_class = si->si_class;
1001	cdf_swap_class(&ssi->si_class);
1002	ssi->si_count = CDF_TOLE4(si->si_count);
1003	*count = 0;
1004	maxcount = 0;
1005	*info = NULL;
1006	if (cdf_read_property_info(sst, h, CDF_TOLE4(sd->sd_offset), info,
1007	    count, &maxcount) == -1)
1008		return -1;
1009	return 0;
1010}
1011
1012
1013#define extract_catalog_field(t, f, l) \
1014    if (b + l + sizeof(cep->f) > eb) { \
1015	    cep->ce_namlen = 0; \
1016	    break; \
1017    } \
1018    memcpy(&cep->f, b + (l), sizeof(cep->f)); \
1019    ce[i].f = CAST(t, CDF_TOLE(cep->f))
1020
1021int
1022cdf_unpack_catalog(const cdf_header_t *h, const cdf_stream_t *sst,
1023    cdf_catalog_t **cat)
1024{
1025	size_t ss = sst->sst_dirlen < h->h_min_size_standard_stream ?
1026	    CDF_SHORT_SEC_SIZE(h) : CDF_SEC_SIZE(h);
1027	const char *b = CAST(const char *, sst->sst_tab);
1028	const char *eb = b + ss * sst->sst_len;
1029	size_t nr, i, j, k;
1030	cdf_catalog_entry_t *ce;
1031	uint16_t reclen;
1032	const uint16_t *np;
1033
1034	for (nr = 0;; nr++) {
1035		memcpy(&reclen, b, sizeof(reclen));
1036		reclen = CDF_TOLE2(reclen);
1037		if (reclen == 0)
1038			break;
1039		b += reclen;
1040		if (b > eb)
1041		    break;
1042	}
1043	nr--;
1044	*cat = CAST(cdf_catalog_t *,
1045	    malloc(sizeof(cdf_catalog_t) + nr * sizeof(*ce)));
1046	ce = (*cat)->cat_e;
1047	memset(ce, 0, nr * sizeof(*ce));
1048	b = CAST(const char *, sst->sst_tab);
1049	for (j = i = 0; i < nr; b += reclen) {
1050		cdf_catalog_entry_t *cep = &ce[j];
1051		uint16_t rlen;
1052
1053		extract_catalog_field(uint16_t, ce_namlen, 0);
1054		extract_catalog_field(uint16_t, ce_num, 4);
1055		extract_catalog_field(uint64_t, ce_timestamp, 8);
1056		reclen = cep->ce_namlen;
1057
1058		if (reclen < 14) {
1059			cep->ce_namlen = 0;
1060			continue;
1061		}
1062
1063		cep->ce_namlen = __arraycount(cep->ce_name) - 1;
1064		rlen = reclen - 14;
1065		if (cep->ce_namlen > rlen)
1066			cep->ce_namlen = rlen;
1067
1068		np = CAST(const uint16_t *, CAST(const void *, (b + 16)));
1069		if (CAST(const char *, np + cep->ce_namlen) > eb) {
1070			cep->ce_namlen = 0;
1071			break;
1072		}
1073
1074		for (k = 0; k < cep->ce_namlen; k++)
1075			cep->ce_name[k] = np[k]; /* XXX: CDF_TOLE2? */
1076		cep->ce_name[cep->ce_namlen] = 0;
1077		j = i;
1078		i++;
1079	}
1080	(*cat)->cat_num = j;
1081	return 0;
1082}
1083
1084int
1085cdf_print_classid(char *buf, size_t buflen, const cdf_classid_t *id)
1086{
1087	return snprintf(buf, buflen, "%.8x-%.4x-%.4x-%.2x%.2x-"
1088	    "%.2x%.2x%.2x%.2x%.2x%.2x", id->cl_dword, id->cl_word[0],
1089	    id->cl_word[1], id->cl_two[0], id->cl_two[1], id->cl_six[0],
1090	    id->cl_six[1], id->cl_six[2], id->cl_six[3], id->cl_six[4],
1091	    id->cl_six[5]);
1092}
1093
1094static const struct {
1095	uint32_t v;
1096	const char *n;
1097} vn[] = {
1098	{ CDF_PROPERTY_CODE_PAGE, "Code page" },
1099	{ CDF_PROPERTY_TITLE, "Title" },
1100	{ CDF_PROPERTY_SUBJECT, "Subject" },
1101	{ CDF_PROPERTY_AUTHOR, "Author" },
1102	{ CDF_PROPERTY_KEYWORDS, "Keywords" },
1103	{ CDF_PROPERTY_COMMENTS, "Comments" },
1104	{ CDF_PROPERTY_TEMPLATE, "Template" },
1105	{ CDF_PROPERTY_LAST_SAVED_BY, "Last Saved By" },
1106	{ CDF_PROPERTY_REVISION_NUMBER, "Revision Number" },
1107	{ CDF_PROPERTY_TOTAL_EDITING_TIME, "Total Editing Time" },
1108	{ CDF_PROPERTY_LAST_PRINTED, "Last Printed" },
1109	{ CDF_PROPERTY_CREATE_TIME, "Create Time/Date" },
1110	{ CDF_PROPERTY_LAST_SAVED_TIME, "Last Saved Time/Date" },
1111	{ CDF_PROPERTY_NUMBER_OF_PAGES, "Number of Pages" },
1112	{ CDF_PROPERTY_NUMBER_OF_WORDS, "Number of Words" },
1113	{ CDF_PROPERTY_NUMBER_OF_CHARACTERS, "Number of Characters" },
1114	{ CDF_PROPERTY_THUMBNAIL, "Thumbnail" },
1115	{ CDF_PROPERTY_NAME_OF_APPLICATION, "Name of Creating Application" },
1116	{ CDF_PROPERTY_SECURITY, "Security" },
1117	{ CDF_PROPERTY_LOCALE_ID, "Locale ID" },
1118};
1119
1120int
1121cdf_print_property_name(char *buf, size_t bufsiz, uint32_t p)
1122{
1123	size_t i;
1124
1125	for (i = 0; i < __arraycount(vn); i++)
1126		if (vn[i].v == p)
1127			return snprintf(buf, bufsiz, "%s", vn[i].n);
1128	return snprintf(buf, bufsiz, "0x%x", p);
1129}
1130
1131int
1132cdf_print_elapsed_time(char *buf, size_t bufsiz, cdf_timestamp_t ts)
1133{
1134	int len = 0;
1135	int days, hours, mins, secs;
1136
1137	ts /= CDF_TIME_PREC;
1138	secs = (int)(ts % 60);
1139	ts /= 60;
1140	mins = (int)(ts % 60);
1141	ts /= 60;
1142	hours = (int)(ts % 24);
1143	ts /= 24;
1144	days = (int)ts;
1145
1146	if (days) {
1147		len += snprintf(buf + len, bufsiz - len, "%dd+", days);
1148		if ((size_t)len >= bufsiz)
1149			return len;
1150	}
1151
1152	if (days || hours) {
1153		len += snprintf(buf + len, bufsiz - len, "%.2d:", hours);
1154		if ((size_t)len >= bufsiz)
1155			return len;
1156	}
1157
1158	len += snprintf(buf + len, bufsiz - len, "%.2d:", mins);
1159	if ((size_t)len >= bufsiz)
1160		return len;
1161
1162	len += snprintf(buf + len, bufsiz - len, "%.2d", secs);
1163	return len;
1164}
1165
1166char *
1167cdf_u16tos8(char *buf, size_t len, const uint16_t *p)
1168{
1169	size_t i;
1170	for (i = 0; i < len && p[i]; i++)
1171		buf[i] = (char)p[i];
1172	buf[i] = '\0';
1173	return buf;
1174}
1175
1176#ifdef CDF_DEBUG
1177void
1178cdf_dump_header(const cdf_header_t *h)
1179{
1180	size_t i;
1181
1182#define DUMP(a, b) (void)fprintf(stderr, "%40.40s = " a "\n", # b, h->h_ ## b)
1183#define DUMP2(a, b) (void)fprintf(stderr, "%40.40s = " a " (" a ")\n", # b, \
1184    h->h_ ## b, 1 << h->h_ ## b)
1185	DUMP("%d", revision);
1186	DUMP("%d", version);
1187	DUMP("0x%x", byte_order);
1188	DUMP2("%d", sec_size_p2);
1189	DUMP2("%d", short_sec_size_p2);
1190	DUMP("%d", num_sectors_in_sat);
1191	DUMP("%d", secid_first_directory);
1192	DUMP("%d", min_size_standard_stream);
1193	DUMP("%d", secid_first_sector_in_short_sat);
1194	DUMP("%d", num_sectors_in_short_sat);
1195	DUMP("%d", secid_first_sector_in_master_sat);
1196	DUMP("%d", num_sectors_in_master_sat);
1197	for (i = 0; i < __arraycount(h->h_master_sat); i++) {
1198		if (h->h_master_sat[i] == CDF_SECID_FREE)
1199			break;
1200		(void)fprintf(stderr, "%35.35s[%.3" SIZE_T_FORMAT "u] = %d\n",
1201		    "master_sat", i, h->h_master_sat[i]);
1202	}
1203}
1204
1205void
1206cdf_dump_sat(const char *prefix, const cdf_sat_t *sat, size_t size)
1207{
1208	size_t i, j, s = size / sizeof(cdf_secid_t);
1209
1210	for (i = 0; i < sat->sat_len; i++) {
1211		(void)fprintf(stderr, "%s[%" SIZE_T_FORMAT "u]:\n%.6"
1212		    SIZE_T_FORMAT "u: ", prefix, i, i * s);
1213		for (j = 0; j < s; j++) {
1214			(void)fprintf(stderr, "%5d, ",
1215			    CDF_TOLE4(sat->sat_tab[s * i + j]));
1216			if ((j + 1) % 10 == 0)
1217				(void)fprintf(stderr, "\n%.6" SIZE_T_FORMAT
1218				    "u: ", i * s + j + 1);
1219		}
1220		(void)fprintf(stderr, "\n");
1221	}
1222}
1223
1224void
1225cdf_dump(const void *v, size_t len)
1226{
1227	size_t i, j;
1228	const unsigned char *p = v;
1229	char abuf[16];
1230
1231	(void)fprintf(stderr, "%.4x: ", 0);
1232	for (i = 0, j = 0; i < len; i++, p++) {
1233		(void)fprintf(stderr, "%.2x ", *p);
1234		abuf[j++] = isprint(*p) ? *p : '.';
1235		if (j == 16) {
1236			j = 0;
1237			abuf[15] = '\0';
1238			(void)fprintf(stderr, "%s\n%.4" SIZE_T_FORMAT "x: ",
1239			    abuf, i + 1);
1240		}
1241	}
1242	(void)fprintf(stderr, "\n");
1243}
1244
1245void
1246cdf_dump_stream(const cdf_header_t *h, const cdf_stream_t *sst)
1247{
1248	size_t ss = sst->sst_dirlen < h->h_min_size_standard_stream ?
1249	    CDF_SHORT_SEC_SIZE(h) : CDF_SEC_SIZE(h);
1250	cdf_dump(sst->sst_tab, ss * sst->sst_len);
1251}
1252
1253void
1254cdf_dump_dir(const cdf_info_t *info, const cdf_header_t *h,
1255    const cdf_sat_t *sat, const cdf_sat_t *ssat, const cdf_stream_t *sst,
1256    const cdf_dir_t *dir)
1257{
1258	size_t i, j;
1259	cdf_directory_t *d;
1260	char name[__arraycount(d->d_name)];
1261	cdf_stream_t scn;
1262	struct timespec ts;
1263
1264	static const char *types[] = { "empty", "user storage",
1265	    "user stream", "lockbytes", "property", "root storage" };
1266
1267	for (i = 0; i < dir->dir_len; i++) {
1268		char buf[26];
1269		d = &dir->dir_tab[i];
1270		for (j = 0; j < sizeof(name); j++)
1271			name[j] = (char)CDF_TOLE2(d->d_name[j]);
1272		(void)fprintf(stderr, "Directory %" SIZE_T_FORMAT "u: %s\n",
1273		    i, name);
1274		if (d->d_type < __arraycount(types))
1275			(void)fprintf(stderr, "Type: %s\n", types[d->d_type]);
1276		else
1277			(void)fprintf(stderr, "Type: %d\n", d->d_type);
1278		(void)fprintf(stderr, "Color: %s\n",
1279		    d->d_color ? "black" : "red");
1280		(void)fprintf(stderr, "Left child: %d\n", d->d_left_child);
1281		(void)fprintf(stderr, "Right child: %d\n", d->d_right_child);
1282		(void)fprintf(stderr, "Flags: 0x%x\n", d->d_flags);
1283		cdf_timestamp_to_timespec(&ts, d->d_created);
1284		(void)fprintf(stderr, "Created %s", cdf_ctime(&ts.tv_sec, buf));
1285		cdf_timestamp_to_timespec(&ts, d->d_modified);
1286		(void)fprintf(stderr, "Modified %s",
1287		    cdf_ctime(&ts.tv_sec, buf));
1288		(void)fprintf(stderr, "Stream %d\n", d->d_stream_first_sector);
1289		(void)fprintf(stderr, "Size %d\n", d->d_size);
1290		switch (d->d_type) {
1291		case CDF_DIR_TYPE_USER_STORAGE:
1292			(void)fprintf(stderr, "Storage: %d\n", d->d_storage);
1293			break;
1294		case CDF_DIR_TYPE_USER_STREAM:
1295			if (sst == NULL)
1296				break;
1297			if (cdf_read_sector_chain(info, h, sat, ssat, sst,
1298			    d->d_stream_first_sector, d->d_size, &scn) == -1) {
1299				warn("Can't read stream for %s at %d len %d",
1300				    name, d->d_stream_first_sector, d->d_size);
1301				break;
1302			}
1303			cdf_dump_stream(h, &scn);
1304			free(scn.sst_tab);
1305			break;
1306		default:
1307			break;
1308		}
1309
1310	}
1311}
1312
1313void
1314cdf_dump_property_info(const cdf_property_info_t *info, size_t count)
1315{
1316	cdf_timestamp_t tp;
1317	struct timespec ts;
1318	char buf[64];
1319	size_t i, j;
1320
1321	for (i = 0; i < count; i++) {
1322		cdf_print_property_name(buf, sizeof(buf), info[i].pi_id);
1323		(void)fprintf(stderr, "%" SIZE_T_FORMAT "u) %s: ", i, buf);
1324		switch (info[i].pi_type) {
1325		case CDF_NULL:
1326			break;
1327		case CDF_SIGNED16:
1328			(void)fprintf(stderr, "signed 16 [%hd]\n",
1329			    info[i].pi_s16);
1330			break;
1331		case CDF_SIGNED32:
1332			(void)fprintf(stderr, "signed 32 [%d]\n",
1333			    info[i].pi_s32);
1334			break;
1335		case CDF_UNSIGNED32:
1336			(void)fprintf(stderr, "unsigned 32 [%u]\n",
1337			    info[i].pi_u32);
1338			break;
1339		case CDF_FLOAT:
1340			(void)fprintf(stderr, "float [%g]\n",
1341			    info[i].pi_f);
1342			break;
1343		case CDF_DOUBLE:
1344			(void)fprintf(stderr, "double [%g]\n",
1345			    info[i].pi_d);
1346			break;
1347		case CDF_LENGTH32_STRING:
1348			(void)fprintf(stderr, "string %u [%.*s]\n",
1349			    info[i].pi_str.s_len,
1350			    info[i].pi_str.s_len, info[i].pi_str.s_buf);
1351			break;
1352		case CDF_LENGTH32_WSTRING:
1353			(void)fprintf(stderr, "string %u [",
1354			    info[i].pi_str.s_len);
1355			for (j = 0; j < info[i].pi_str.s_len - 1; j++)
1356			    (void)fputc(info[i].pi_str.s_buf[j << 1], stderr);
1357			(void)fprintf(stderr, "]\n");
1358			break;
1359		case CDF_FILETIME:
1360			tp = info[i].pi_tp;
1361			if (tp < 1000000000000000LL) {
1362				cdf_print_elapsed_time(buf, sizeof(buf), tp);
1363				(void)fprintf(stderr, "timestamp %s\n", buf);
1364			} else {
1365				char tbuf[26];
1366				cdf_timestamp_to_timespec(&ts, tp);
1367				(void)fprintf(stderr, "timestamp %s",
1368				    cdf_ctime(&ts.tv_sec, tbuf));
1369			}
1370			break;
1371		case CDF_CLIPBOARD:
1372			(void)fprintf(stderr, "CLIPBOARD %u\n", info[i].pi_u32);
1373			break;
1374		default:
1375			DPRINTF(("Don't know how to deal with %x\n",
1376			    info[i].pi_type));
1377			break;
1378		}
1379	}
1380}
1381
1382
1383void
1384cdf_dump_summary_info(const cdf_header_t *h, const cdf_stream_t *sst)
1385{
1386	char buf[128];
1387	cdf_summary_info_header_t ssi;
1388	cdf_property_info_t *info;
1389	size_t count;
1390
1391	(void)&h;
1392	if (cdf_unpack_summary_info(sst, h, &ssi, &info, &count) == -1)
1393		return;
1394	(void)fprintf(stderr, "Endian: %x\n", ssi.si_byte_order);
1395	(void)fprintf(stderr, "Os Version %d.%d\n", ssi.si_os_version & 0xff,
1396	    ssi.si_os_version >> 8);
1397	(void)fprintf(stderr, "Os %d\n", ssi.si_os);
1398	cdf_print_classid(buf, sizeof(buf), &ssi.si_class);
1399	(void)fprintf(stderr, "Class %s\n", buf);
1400	(void)fprintf(stderr, "Count %d\n", ssi.si_count);
1401	cdf_dump_property_info(info, count);
1402	free(info);
1403}
1404
1405
1406void
1407cdf_dump_catalog(const cdf_header_t *h, const cdf_stream_t *sst)
1408{
1409	cdf_catalog_t *cat;
1410	cdf_unpack_catalog(h, sst, &cat);
1411	const cdf_catalog_entry_t *ce = cat->cat_e;
1412	struct timespec ts;
1413	char tbuf[64], sbuf[256];
1414	size_t i;
1415
1416	printf("Catalog:\n");
1417	for (i = 0; i < cat->cat_num; i++) {
1418		cdf_timestamp_to_timespec(&ts, ce[i].ce_timestamp);
1419		printf("\t%d %s %s", ce[i].ce_num,
1420		    cdf_u16tos8(sbuf, ce[i].ce_namlen, ce[i].ce_name),
1421		    cdf_ctime(&ts.tv_sec, tbuf));
1422	}
1423	free(cat);
1424}
1425
1426#endif
1427
1428#ifdef TEST
1429int
1430main(int argc, char *argv[])
1431{
1432	int i;
1433	cdf_header_t h;
1434	cdf_sat_t sat, ssat;
1435	cdf_stream_t sst, scn;
1436	cdf_dir_t dir;
1437	cdf_info_t info;
1438	const cdf_directory_t *root;
1439#ifdef __linux__
1440#define getprogname() __progname
1441	extern char *__progname;
1442#endif
1443	if (argc < 2) {
1444		(void)fprintf(stderr, "Usage: %s <filename>\n", getprogname());
1445		return -1;
1446	}
1447
1448	info.i_buf = NULL;
1449	info.i_len = 0;
1450	for (i = 1; i < argc; i++) {
1451		if ((info.i_fd = open(argv[1], O_RDONLY)) == -1)
1452			err(1, "Cannot open `%s'", argv[1]);
1453
1454		if (cdf_read_header(&info, &h) == -1)
1455			err(1, "Cannot read header");
1456#ifdef CDF_DEBUG
1457		cdf_dump_header(&h);
1458#endif
1459
1460		if (cdf_read_sat(&info, &h, &sat) == -1)
1461			err(1, "Cannot read sat");
1462#ifdef CDF_DEBUG
1463		cdf_dump_sat("SAT", &sat, CDF_SEC_SIZE(&h));
1464#endif
1465
1466		if (cdf_read_ssat(&info, &h, &sat, &ssat) == -1)
1467			err(1, "Cannot read ssat");
1468#ifdef CDF_DEBUG
1469		cdf_dump_sat("SSAT", &ssat, CDF_SHORT_SEC_SIZE(&h));
1470#endif
1471
1472		if (cdf_read_dir(&info, &h, &sat, &dir) == -1)
1473			err(1, "Cannot read dir");
1474
1475		if (cdf_read_short_stream(&info, &h, &sat, &dir, &sst, &root)
1476		    == -1)
1477			err(1, "Cannot read short stream");
1478#ifdef CDF_DEBUG
1479		cdf_dump_stream(&h, &sst);
1480#endif
1481
1482#ifdef CDF_DEBUG
1483		cdf_dump_dir(&info, &h, &sat, &ssat, &sst, &dir);
1484#endif
1485
1486
1487		if (cdf_read_summary_info(&info, &h, &sat, &ssat, &sst, &dir,
1488		    &scn) == -1)
1489			warn("Cannot read summary info");
1490#ifdef CDF_DEBUG
1491		else
1492			cdf_dump_summary_info(&h, &scn);
1493#endif
1494		if (cdf_read_user_stream(&info, &h, &sat, &ssat, &sst,
1495		    &dir, "Catalog", &scn) == -1)
1496			warn("Cannot read catalog");
1497#ifdef CDF_DEBUG
1498		else
1499			cdf_dump_catalog(&h, &scn);
1500#endif
1501
1502		(void)close(info.i_fd);
1503	}
1504
1505	return 0;
1506}
1507#endif
1508