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