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