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