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