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