Coverage Report

Created: 2026-07-24 07:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/xps/xpszip.c
Line
Count
Source
1
/* Copyright (C) 2001-2026 Artifex Software, Inc.
2
   All Rights Reserved.
3
4
   This software is provided AS-IS with no warranty, either express or
5
   implied.
6
7
   This software is distributed under license and may not be copied,
8
   modified or distributed except as expressly authorized under the terms
9
   of the license contained in the file LICENSE in this distribution.
10
11
   Refer to licensing information at http://www.artifex.com or contact
12
   Artifex Software, Inc.,  39 Mesa Street, Suite 108A, San Francisco,
13
   CA 94129, USA, for further information.
14
*/
15
16
17
/* XPS interpreter - zip container parsing */
18
19
#include "ghostxps.h"
20
#include "pagelist.h"
21
22
static int isfile(gs_memory_t *mem, char *path)
23
0
{
24
0
    gp_file *file = gp_fopen(mem, path, "rb");
25
0
    if (file)
26
0
    {
27
0
        gp_fclose(file);
28
0
        return 1;
29
0
    }
30
0
    return 0;
31
0
}
32
33
static inline int getshort(gp_file *file)
34
17.3k
{
35
17.3k
    int a = xps_getc(file);
36
17.3k
    int b = xps_getc(file);
37
17.3k
    return a | (b << 8);
38
17.3k
}
39
40
static inline int getlong(gp_file *file)
41
9.67k
{
42
9.67k
    unsigned int a = (unsigned int)xps_getc(file);
43
9.67k
    unsigned int b = (unsigned int)xps_getc(file);
44
9.67k
    unsigned int c = (unsigned int)xps_getc(file);
45
9.67k
    unsigned int d = (unsigned int)xps_getc(file);
46
9.67k
    return a | (b << 8) | (c << 16) | (d << 24);
47
9.67k
}
48
49
static void *
50
xps_zip_alloc_items(xps_context_t *ctx, int items, int size)
51
473
{
52
473
    void *item;
53
473
    uint32_t total;
54
55
473
    if (check_uint32_multiply((uint32_t)items, (uint32_t)size, &total) != 0) {
56
0
        gs_throw(gs_error_limitcheck, "item is too large");
57
0
        return NULL;
58
0
    }
59
60
473
    item = xps_alloc(ctx, total);
61
473
    if (!item) {
62
0
        gs_throw(gs_error_VMerror, "out of memory: item.\n");
63
0
        return NULL;
64
0
    }
65
473
    return item;
66
473
}
67
68
static void
69
xps_zip_free(xps_context_t *ctx, void *ptr)
70
473
{
71
473
    xps_free(ctx, ptr);
72
473
}
73
74
static int
75
xps_compare_entries(const void *a0, const void *b0)
76
2.98k
{
77
2.98k
    xps_entry_t *a = (xps_entry_t*) a0;
78
2.98k
    xps_entry_t *b = (xps_entry_t*) b0;
79
2.98k
    return xps_strcasecmp(a->name, b->name);
80
2.98k
}
81
82
static xps_entry_t *
83
xps_find_zip_entry(xps_context_t *ctx, const char *name)
84
484
{
85
484
    int l = 0;
86
484
    int r = ctx->zip_count - 1;
87
1.60k
    while (l <= r)
88
1.59k
    {
89
1.59k
        int m = (l + r) >> 1;
90
1.59k
        int c = xps_strcasecmp(name, ctx->zip_table[m].name);
91
1.59k
        if (c < 0)
92
386
            r = m - 1;
93
1.21k
        else if (c > 0)
94
736
            l = m + 1;
95
475
        else
96
475
            return &ctx->zip_table[m];
97
1.59k
    }
98
9
    return NULL;
99
484
}
100
101
/*
102
 * Inflate the data in a zip entry.
103
 */
104
105
static int
106
xps_read_zip_entry(xps_context_t *ctx, xps_entry_t *ent, unsigned char *outbuf)
107
475
{
108
475
    z_stream stream;
109
475
    unsigned char *inbuf;
110
475
    int sig;
111
475
    int version, general, method;
112
475
    int namelength, extralength;
113
475
    int code;
114
115
475
    if_debug1m('|', ctx->memory, "zip: inflating entry '%s'\n", ent->name);
116
117
475
    if (xps_fseek(ctx->file, ent->offset, 0) < 0)
118
0
        return gs_throw1(-1, "seek to offset %d failed.", ent->offset);
119
120
475
    sig = getlong(ctx->file);
121
475
    if (sig != ZIP_LOCAL_FILE_SIG)
122
1
        return gs_throw1(-1, "wrong zip local file signature (0x%x)", sig);
123
124
474
    version = getshort(ctx->file);
125
474
    (void)version; /* Avoid unused variable compiler warning */
126
474
    general = getshort(ctx->file);
127
474
    if (general & ZIP_ENCRYPTED_FLAG)
128
0
        return gs_throw(-1, "zip file content is encrypted");
129
474
    method = getshort(ctx->file);
130
474
    (void) getshort(ctx->file); /* file time */
131
474
    (void) getshort(ctx->file); /* file date */
132
474
    (void) getlong(ctx->file); /* crc-32 */
133
474
    (void) getlong(ctx->file); /* csize */
134
474
    (void) getlong(ctx->file); /* usize */
135
474
    namelength = getshort(ctx->file);
136
474
    extralength = getshort(ctx->file);
137
138
474
    if (namelength < 0 || namelength > 65535)
139
0
        return gs_rethrow(gs_error_ioerror, "Illegal namelength (can't happen).\n");
140
474
    if (extralength < 0 || extralength > 65535)
141
0
        return gs_rethrow(gs_error_ioerror, "Illegal extralength (can't happen).\n");
142
143
474
    if (xps_fseek(ctx->file, namelength + extralength, 1) != 0)
144
0
        return gs_throw1(gs_error_ioerror, "xps_fseek to %d failed.\n", namelength + extralength);
145
146
474
    if (method == 0)
147
0
    {
148
0
        code = xps_fread(outbuf, 1, ent->usize, ctx->file);
149
0
        if (code != ent->usize)
150
0
            return gs_throw1(gs_error_ioerror, "Failed to read %d bytes", ent->usize);
151
0
    }
152
474
    else if (method == 8)
153
473
    {
154
473
        inbuf = xps_alloc(ctx, ent->csize);
155
473
        if (!inbuf) {
156
0
            return gs_rethrow(gs_error_VMerror, "out of memory.\n");
157
0
        }
158
159
473
        code = xps_fread(inbuf, 1, ent->csize, ctx->file);
160
473
        if (code != ent->csize) {
161
0
            xps_free(ctx, inbuf);
162
0
            return gs_throw1(gs_error_ioerror, "Failed to read %d bytes", ent->csize);
163
0
        }
164
165
473
        memset(&stream, 0, sizeof(z_stream));
166
473
        stream.zalloc = (alloc_func) xps_zip_alloc_items;
167
473
        stream.zfree = (free_func) xps_zip_free;
168
473
        stream.opaque = ctx;
169
473
        stream.next_in = inbuf;
170
473
        stream.avail_in = ent->csize;
171
473
        stream.next_out = outbuf;
172
473
        stream.avail_out = ent->usize;
173
174
473
        code = inflateInit2(&stream, -15);
175
473
        if (code != Z_OK)
176
0
        {
177
0
            xps_free(ctx, inbuf);
178
0
            return gs_throw1(-1, "zlib inflateInit2 error: %s", stream.msg);
179
0
        }
180
473
        code = inflate(&stream, Z_FINISH);
181
473
        if (code != Z_STREAM_END)
182
3
        {
183
3
            inflateEnd(&stream);
184
3
            xps_free(ctx, inbuf);
185
3
            return gs_throw1(-1, "zlib inflate error: %s", stream.msg);
186
3
        }
187
470
        code = inflateEnd(&stream);
188
470
        if (code != Z_OK)
189
0
        {
190
0
            xps_free(ctx, inbuf);
191
0
            return gs_throw1(-1, "zlib inflateEnd error: %s", stream.msg);
192
0
        }
193
194
470
        xps_free(ctx, inbuf);
195
196
        /* If the stream has less data than advertised, then zero the remainder. */
197
470
        if (stream.avail_out > 0)
198
85
        {
199
85
            gs_warn("truncated zipfile entry; possibly corrupt data");
200
85
            memset(stream.next_out, 0, stream.avail_out);
201
85
        }
202
470
    }
203
1
    else
204
1
    {
205
1
        return gs_throw1(-1, "unknown compression method (%d)", method);
206
1
    }
207
208
470
    return gs_okay;
209
474
}
210
211
/*
212
 * Read the central directory in a zip file.
213
 */
214
215
static int
216
xps_read_zip_dir(xps_context_t *ctx, int start_offset)
217
128
{
218
128
    int sig;
219
128
    int offset, count, read;
220
128
    int namesize, metasize, commentsize;
221
128
    int i;
222
223
128
    if (xps_fseek(ctx->file, start_offset, 0) != 0)
224
0
        return gs_throw1(gs_error_ioerror, "xps_fseek to %d failed.", start_offset);
225
226
128
    sig = getlong(ctx->file);
227
128
    if (sig != ZIP_END_OF_CENTRAL_DIRECTORY_SIG)
228
0
        return gs_throw1(-1, "wrong zip end of central directory signature (0x%x)", sig);
229
230
128
    (void) getshort(ctx->file); /* this disk */
231
128
    (void) getshort(ctx->file); /* start disk */
232
128
    (void) getshort(ctx->file); /* entries in this disk */
233
128
    count = getshort(ctx->file); /* entries in central directory disk */
234
128
    (void) getlong(ctx->file); /* size of central directory */
235
128
    offset = getlong(ctx->file); /* offset to central directory */
236
237
128
    if (count < 0 || count > 65535)
238
0
        return gs_rethrow(gs_error_rangecheck, "invalid number of entries in central directory disk (can't happen)");
239
240
128
    ctx->zip_count = count;
241
128
    if (count > INT_MAX / sizeof(xps_entry_t *))
242
0
        return gs_rethrow(gs_error_limitcheck, "entry table too large");
243
128
    ctx->zip_table = xps_alloc(ctx, sizeof(xps_entry_t) * (size_t)count);
244
128
    if (!ctx->zip_table)
245
0
        return gs_rethrow(gs_error_VMerror, "cannot allocate zip entry table");
246
247
128
    memset(ctx->zip_table, 0, sizeof(xps_entry_t) * count);
248
249
128
    if (xps_fseek(ctx->file, offset, 0) != 0)
250
1
        return gs_throw1(gs_error_ioerror, "xps_fseek to offset %d failed", offset);
251
252
1.35k
    for (i = 0; i < count; i++)
253
1.23k
    {
254
1.23k
        sig = getlong(ctx->file);
255
1.23k
        if (sig != ZIP_CENTRAL_DIRECTORY_SIG)
256
6
            return gs_throw1(-1, "wrong zip central directory signature (0x%x)", sig);
257
258
1.23k
        (void) getshort(ctx->file); /* version made by */
259
1.23k
        (void) getshort(ctx->file); /* version to extract */
260
1.23k
        (void) getshort(ctx->file); /* general */
261
1.23k
        (void) getshort(ctx->file); /* method */
262
1.23k
        (void) getshort(ctx->file); /* last mod file time */
263
1.23k
        (void) getshort(ctx->file); /* last mod file date */
264
1.23k
        (void) getlong(ctx->file); /* crc-32 */
265
1.23k
        ctx->zip_table[i].csize = getlong(ctx->file);
266
1.23k
        ctx->zip_table[i].usize = getlong(ctx->file);
267
1.23k
        namesize = getshort(ctx->file);
268
1.23k
        if (namesize < 0 || namesize > 65535)
269
0
            return gs_rethrow(gs_error_rangecheck, "illegal namesize (can't happen)");
270
1.23k
        metasize = getshort(ctx->file);
271
1.23k
        commentsize = getshort(ctx->file);
272
1.23k
        (void) getshort(ctx->file); /* disk number start */
273
1.23k
        (void) getshort(ctx->file); /* int file atts */
274
1.23k
        (void) getlong(ctx->file); /* ext file atts */
275
1.23k
        ctx->zip_table[i].offset = getlong(ctx->file);
276
277
1.23k
        if (ctx->zip_table[i].csize < 0 || ctx->zip_table[i].usize < 0)
278
2
            return gs_throw(gs_error_ioerror, "cannot read zip entries larger than 2GB");
279
280
1.23k
        if (namesize == INT_MAX)
281
0
            return gs_rethrow(gs_error_VMerror, "cannot allocate zip entry name");
282
1.23k
        ctx->zip_table[i].name = xps_alloc(ctx, (size_t)namesize + 1);
283
1.23k
        if (!ctx->zip_table[i].name)
284
0
            return gs_rethrow(gs_error_VMerror, "cannot allocate zip entry name");
285
286
1.23k
        read = xps_fread(ctx->zip_table[i].name, 1, (size_t)namesize, ctx->file);
287
1.23k
        if (read != namesize)
288
2
            return gs_throw1(gs_error_ioerror, "failed to read %d bytes", namesize);
289
290
1.22k
        ctx->zip_table[i].name[namesize] = 0;
291
292
1.22k
        if (xps_fseek(ctx->file, metasize, 1) != 0)
293
0
            return gs_throw1(gs_error_ioerror, "xps_fseek to offset %d failed", metasize);
294
1.22k
        if (xps_fseek(ctx->file, commentsize, 1) != 0)
295
0
            return gs_throw1(gs_error_ioerror, "xps_fseek to offset %d failed", commentsize);
296
1.22k
    }
297
298
117
    qsort(ctx->zip_table, count, sizeof(xps_entry_t), xps_compare_entries);
299
300
1.31k
    for (i = 0; i < ctx->zip_count; i++)
301
1.20k
    {
302
1.20k
        if_debug3m('|', ctx->memory, "zip entry '%s' csize=%d usize=%d\n",
303
1.20k
                   ctx->zip_table[i].name,
304
1.20k
                   ctx->zip_table[i].csize,
305
1.20k
                   ctx->zip_table[i].usize);
306
1.20k
    }
307
308
117
    return gs_okay;
309
127
}
310
311
static int
312
xps_find_and_read_zip_dir(xps_context_t *ctx)
313
147
{
314
147
    int filesize, back, maxback;
315
147
    int i, n;
316
147
    char buf[512];
317
318
147
    if (xps_fseek(ctx->file, 0, SEEK_END) < 0)
319
0
        return gs_throw(-1, "seek to end failed.");
320
321
147
    filesize = xps_ftell(ctx->file);
322
323
147
    maxback = MIN(filesize, 0xFFFF + sizeof buf);
324
147
    back = MIN(maxback, sizeof buf);
325
326
568
    while (back < maxback)
327
549
    {
328
549
        if (xps_fseek(ctx->file, filesize - back, 0) < 0)
329
0
            return gs_throw1(gs_error_ioerror, "xps_fseek to %d failed.\n", filesize - back);
330
331
549
        n = xps_fread(buf, 1, sizeof buf, ctx->file);
332
549
        if (n < 0)
333
0
            return gs_throw(-1, "cannot read end of central directory");
334
335
217k
        for (i = n - 4; i > 0; i--)
336
217k
            if (!memcmp(buf + i, "PK\5\6", 4))
337
128
                return xps_read_zip_dir(ctx, filesize - back + i);
338
339
421
        back += sizeof buf - 4;
340
421
    }
341
342
19
    return gs_throw(-1, "cannot find end of central directory");
343
147
}
344
345
/*
346
 * Read and interleave split parts from a ZIP file.
347
 */
348
349
static xps_part_t *
350
xps_read_zip_part(xps_context_t *ctx, const char *partname)
351
478
{
352
478
    char buf[2048];
353
478
    xps_entry_t *ent;
354
478
    xps_part_t *part;
355
478
    int count, size, offset, i;
356
478
    int code = 0;
357
478
    const char *name;
358
478
    int seen_last = 0;
359
360
478
    name = partname;
361
478
    if (name[0] == '/')
362
478
        name ++;
363
364
    /* All in one piece */
365
478
    ent = xps_find_zip_entry(ctx, name);
366
478
    if (ent)
367
475
    {
368
475
        part = xps_new_part(ctx, partname, ent->usize);
369
475
        if (part != NULL)
370
475
            code = xps_read_zip_entry(ctx, ent, part->data);
371
0
        else
372
0
            return NULL;
373
475
        if (code < 0)
374
5
        {
375
5
            xps_free_part(ctx, part);
376
5
            gs_rethrow1(-1, "cannot read zip entry '%s'", name);
377
5
            return NULL;
378
5
        }
379
470
        return part;
380
475
    }
381
382
    /* Count the number of pieces and their total size */
383
3
    count = 0;
384
3
    size = 0;
385
3
    while (!seen_last)
386
3
    {
387
3
        gs_snprintf(buf, sizeof(buf), "%s/[%d].piece", name, count);
388
3
        ent = xps_find_zip_entry(ctx, buf);
389
3
        if (!ent)
390
3
        {
391
3
            gs_snprintf(buf, sizeof(buf), "%s/[%d].last.piece", name, count);
392
3
            ent = xps_find_zip_entry(ctx, buf);
393
3
            seen_last = !!ent;
394
3
        }
395
3
        if (!ent)
396
3
            break;
397
0
        count ++;
398
        /* check for integer overflow */
399
0
        if (size > INT_MAX - ent->usize)
400
0
        {
401
0
            gs_throw1(-1, "part '%s' is too large", partname);
402
0
            return NULL;
403
0
        }
404
0
        size += ent->usize;
405
0
    }
406
3
    if (!seen_last)
407
3
    {
408
3
        gs_throw1(-1, "cannot find all pieces for part '%s'", partname);
409
3
        return NULL;
410
3
    }
411
412
    /* Inflate the pieces */
413
0
    if (count)
414
0
    {
415
0
        part = xps_new_part(ctx, partname, size);
416
0
        offset = 0;
417
0
        for (i = 0; i < count; i++)
418
0
        {
419
0
            if (i < count - 1)
420
0
                gs_snprintf(buf, sizeof(buf), "%s/[%d].piece", name, i);
421
0
            else
422
0
                gs_snprintf(buf, sizeof(buf), "%s/[%d].last.piece", name, i);
423
0
            ent = xps_find_zip_entry(ctx, buf);
424
0
            if (!ent)
425
0
                gs_warn("missing piece");
426
0
            else
427
0
            {
428
0
                code = xps_read_zip_entry(ctx, ent, part->data + offset);
429
0
                if (code < 0)
430
0
                {
431
0
                    xps_free_part(ctx, part);
432
0
                    gs_rethrow1(-1, "cannot read zip entry '%s'", buf);
433
0
                    return NULL;
434
0
                }
435
0
                offset += ent->usize;
436
0
            }
437
0
        }
438
0
        return part;
439
0
    }
440
441
0
    return NULL;
442
0
}
443
444
/*
445
 * Read and interleave split parts from files in the directory.
446
 */
447
448
static xps_part_t *
449
xps_read_dir_part(xps_context_t *ctx, const char *name)
450
0
{
451
0
    char buf[2048];
452
0
    xps_part_t *part;
453
0
    gp_file *file;
454
0
    int count, size, offset, i, n;
455
0
    int seen_last = 0;
456
457
0
    gs_strlcpy(buf, ctx->directory, sizeof buf);
458
0
    gs_strlcat(buf, name, sizeof buf);
459
460
    /* All in one piece */
461
0
    file = gp_fopen(ctx->memory, buf, "rb");
462
0
    if (file)
463
0
    {
464
0
        if (xps_fseek(file, 0, SEEK_END) != 0) {
465
0
            gp_fclose(file);
466
0
            return NULL;
467
0
        }
468
469
0
        size = xps_ftell(file);
470
0
        if (size < 0) {
471
0
            gp_fclose(file);
472
0
            return NULL;
473
0
        }
474
475
0
        if (xps_fseek(file, 0, SEEK_SET) != 0) {
476
0
            gp_fclose(file);
477
0
            return NULL;
478
0
        }
479
480
0
        part = xps_new_part(ctx, name, size);
481
0
        count = xps_fread(part->data, 1, size, file);
482
0
        gp_fclose(file);
483
0
        if (count == size)
484
0
            return part;
485
0
        else
486
0
            return NULL;
487
0
    }
488
489
    /* Count the number of pieces and their total size */
490
0
    count = 0;
491
0
    size = 0;
492
0
    while (!seen_last)
493
0
    {
494
0
        gs_snprintf(buf, sizeof(buf), "%s%s/[%d].piece", ctx->directory, name, count);
495
0
        file = gp_fopen(ctx->memory, buf, "rb");
496
0
        if (!file)
497
0
        {
498
0
            gs_snprintf(buf, sizeof(buf), "%s%s/[%d].last.piece", ctx->directory, name, count);
499
0
            file = gp_fopen(ctx->memory, buf, "rb");
500
0
            seen_last = !!file;
501
0
        }
502
0
        if (!file)
503
0
            break;
504
0
        count ++;
505
0
        if (xps_fseek(file, 0, SEEK_END) < 0)
506
0
            break;;
507
0
        size += xps_ftell(file);
508
0
        gp_fclose(file);
509
0
    }
510
0
    if (!seen_last)
511
0
    {
512
0
        gs_throw1(-1, "cannot find all pieces for part '%s'", name);
513
0
        return NULL;
514
0
    }
515
516
    /* Inflate the pieces */
517
518
0
    part = xps_new_part(ctx, name, size);
519
0
    if (!part)
520
0
    {
521
0
        gs_rethrow1(-1, "failed to create part '%s'", name);
522
0
        return NULL;
523
0
    }
524
525
0
    offset = 0;
526
0
    for (i = 0; i < count; i++)
527
0
    {
528
0
        if (i < count - 1)
529
0
            gs_snprintf(buf, sizeof(buf), "%s%s/[%d].piece", ctx->directory, name, i);
530
0
        else
531
0
            gs_snprintf(buf, sizeof(buf), "%s%s/[%d].last.piece", ctx->directory, name, i);
532
0
        file = gp_fopen(ctx->memory, buf, "rb");
533
0
        n = xps_fread(part->data + offset, 1, size - offset, file);
534
0
        offset += n;
535
0
        gp_fclose(file);
536
0
    }
537
0
    return part;
538
539
0
}
540
541
xps_part_t *
542
xps_read_part(xps_context_t *ctx, const char *partname)
543
478
{
544
478
    if (ctx->directory)
545
0
        return xps_read_dir_part(ctx, partname);
546
478
    return xps_read_zip_part(ctx, partname);
547
478
}
548
549
/*
550
 * Read and process the XPS document.
551
 */
552
553
static int
554
xps_read_and_process_metadata_part(xps_context_t *ctx, const char *name)
555
344
{
556
344
    xps_part_t *part;
557
344
    int code;
558
559
344
    part = xps_read_part(ctx, name);
560
344
    if (!part)
561
5
        return gs_rethrow1(-1, "cannot read zip part '%s'", name);
562
563
339
    code = xps_parse_metadata(ctx, part);
564
339
    if (code)
565
2
    {
566
2
        xps_free_part(ctx, part);
567
2
        return gs_rethrow1(code, "cannot process metadata part '%s'", name);
568
2
    }
569
570
337
    xps_free_part(ctx, part);
571
572
337
    return gs_okay;
573
339
}
574
575
static int
576
xps_read_and_process_page_part(xps_context_t *ctx, char *name)
577
110
{
578
110
    xps_part_t *part;
579
110
    int code;
580
581
110
    part = xps_read_part(ctx, name);
582
110
    if (!part)
583
2
        return gs_rethrow1(-1, "cannot read zip part '%s'", name);
584
585
108
    code = xps_parse_fixed_page(ctx, part);
586
108
    if (code)
587
86
    {
588
86
        xps_free_part(ctx, part);
589
86
        return gs_rethrow1(code, "cannot parse fixed page part '%s'", name);
590
86
    }
591
592
22
    xps_free_part(ctx, part);
593
594
22
    return gs_okay;
595
108
}
596
597
/* XPS page reordering based upon Device PageList setting */
598
static int
599
xps_reorder_add_page(xps_context_t* ctx, xps_page_t ***page_ptr, xps_page_t* page_to_add)
600
0
{
601
0
    xps_page_t* new_page;
602
603
0
    new_page = xps_alloc(ctx, sizeof(xps_page_t));
604
0
    if (!new_page)
605
0
    {
606
0
        return gs_throw(gs_error_VMerror, "out of memory: xps_reorder_add_page\n");
607
0
    }
608
609
0
    new_page->name = xps_strdup(ctx, page_to_add->name);
610
0
    if (!new_page->name)
611
0
    {
612
0
        return gs_throw(gs_error_VMerror, "out of memory: xps_reorder_add_page\n");
613
0
    }
614
0
    new_page->height = page_to_add->height;
615
0
    new_page->width = page_to_add->width;
616
0
    new_page->next = NULL;
617
618
0
    **page_ptr = new_page;
619
0
    *page_ptr = &(new_page->next);
620
621
0
    return 0;
622
0
}
623
624
625
static int
626
xps_reorder_pages(xps_context_t *ctx)
627
0
{
628
0
    char *page_list = ctx->page_range->page_list;
629
0
    xps_page_t **page_ptr_array, *page = ctx->first_page;
630
0
    int count = 0, k;
631
0
    int code = 0;
632
0
    int start;
633
0
    int end;
634
0
    xps_page_t* first_page = NULL;
635
0
    xps_page_t* last_page;
636
0
    xps_page_t** page_tail = &first_page;
637
0
    int *page_range_array;
638
0
    int ranges_count = 0;
639
640
0
    if (page == NULL)
641
0
        return 0;
642
643
0
    while (page != NULL)
644
0
    {
645
0
        count++;
646
0
        page = page->next;
647
0
    }
648
649
0
    if (count > INT_MAX / sizeof(xps_page_t *))
650
0
        return gs_throw(gs_error_limitcheck, "too many pages\n");
651
    /* Create an array of pointers to the current pages */
652
0
    page_ptr_array = xps_alloc(ctx, (size_t)sizeof(xps_page_t*) * count);
653
0
    if (page_ptr_array == NULL)
654
0
        return gs_throw(gs_error_VMerror, "out of memory: xps_reorder_pages\n");
655
656
0
    page = ctx->first_page;
657
0
    for (k = 0; k < count; k++)
658
0
    {
659
0
        page_ptr_array[k] = page;
660
0
        page = page->next;
661
0
    }
662
663
    /* Use the common function to parse the page_list into a 'page_range_array' */
664
0
    ranges_count = pagelist_parse_to_array(page_list, ctx->memory, count, &page_range_array);
665
0
    if (ranges_count <= 0)
666
0
        return gs_throw(gs_error_typecheck, "Bad page list: xps_reorder_pages\n");
667
668
0
    ranges_count--;     /* ignore the final marker range 0, 0, 0 */
669
670
    /* start processing ranges, ignoring the "ordered" flag at the start of the array */
671
0
    for (k = 1; k < 1 + 3 * ranges_count; k += 3) {
672
0
        start = page_range_array[k+1];
673
0
        end = page_range_array[k+2];
674
675
0
        if (start == end)
676
0
            code = xps_reorder_add_page(ctx, &page_tail, page_ptr_array[start - 1]);
677
0
        else if (start < end) {
678
0
            do {
679
0
                code = xps_reorder_add_page(ctx, &page_tail, page_ptr_array[start - 1]);
680
0
                if (code < 0)
681
0
                    break;
682
0
                start += ((page_range_array[k] == 0) ? 1 : 2);  /* double bump for even/odd */
683
0
            } while (start <= end);
684
0
        } else { /* start > end -- reverse direction */
685
0
            do {
686
0
                code = xps_reorder_add_page(ctx, &page_tail, page_ptr_array[start - 1]);
687
0
                if (code < 0)
688
0
                    break;
689
0
                start -= ((page_range_array[k] == 0) ? 1 : 2);  /* double bump for even/odd */
690
0
            } while (start >= end);
691
0
        }
692
0
    }
693
0
    pagelist_free_range_array(ctx->memory, page_range_array); /* done with all ranges */
694
695
    /* Replace the pages. */
696
0
    if (first_page != NULL)
697
0
    {
698
        /* Set to the last page not its next pointer (Thanks to Robin Watts) */
699
0
        last_page = (xps_page_t*)(((char*)page_tail) - offsetof(xps_page_t, next));
700
701
0
        xps_free_fixed_pages(ctx);
702
0
        xps_free(ctx, page_ptr_array);
703
0
        ctx->first_page = first_page;
704
0
        ctx->last_page = last_page;
705
0
    }
706
0
    else
707
0
        return gs_throw(gs_error_rangecheck, "Bad page list: xps_reorder_pages\n");
708
709
0
    return code;
710
0
}
711
712
/*
713
 * Called by xpstop.c
714
 */
715
716
int
717
xps_process_file(xps_context_t *ctx, const char *filename)
718
147
{
719
147
    char buf[2048];
720
147
    xps_document_t *doc;
721
147
    xps_page_t *page;
722
147
    int code;
723
147
    char *p;
724
725
147
    ctx->file = xps_fopen(ctx->memory, filename, "rb");
726
147
    if (!ctx->file)
727
0
        return gs_throw1(-1, "cannot open file: '%s'", filename);
728
729
147
    if (strstr(filename, ".fpage"))
730
0
    {
731
0
        xps_part_t *part;
732
0
        int size;
733
734
0
        if_debug0m('|', ctx->memory, "zip: single page mode\n");
735
0
        gs_strlcpy(buf, filename, sizeof buf);
736
0
        while (1)
737
0
        {
738
0
            p = strrchr(buf, '/');
739
0
            if (!p)
740
0
                p = strrchr(buf, '\\');
741
0
            if (!p)
742
0
                break;
743
0
            gs_strlcpy(p, "/_rels/.rels", buf + sizeof buf - p);
744
0
            if_debug1m('|', ctx->memory, "zip: testing if '%s' exists\n", buf);
745
0
            if (isfile(ctx->memory, buf))
746
0
            {
747
0
                *p = 0;
748
0
                ctx->directory = xps_strdup(ctx, buf);
749
0
                if_debug1m('|', ctx->memory, "zip: using '%s' as root directory\n", ctx->directory);
750
0
                break;
751
0
            }
752
0
            *p = 0;
753
0
        }
754
0
        if (!ctx->directory)
755
0
        {
756
0
            if_debug0m('|', ctx->memory, "zip: no /_rels/.rels found; assuming absolute paths\n");
757
0
            ctx->directory = xps_strdup(ctx, "");
758
0
        }
759
760
0
        if (xps_fseek(ctx->file, 0, SEEK_END) != 0) {
761
0
            code = gs_rethrow(gs_error_ioerror, "xps_fseek to file end failed");
762
0
            goto cleanup;
763
0
        }
764
765
0
        size = xps_ftell(ctx->file);
766
0
        if (size < 0) {
767
0
            code = gs_rethrow(gs_error_ioerror, "xps_ftell raised an error");
768
0
            goto cleanup;
769
0
        }
770
771
0
        if (xps_fseek(ctx->file, 0, SEEK_SET) != 0) {
772
0
            code = gs_rethrow(gs_error_ioerror, "xps_fseek to file begin failed");
773
0
            goto cleanup;
774
0
        }
775
776
0
        part = xps_new_part(ctx, filename, size);
777
0
        code = xps_fread(part->data, 1, size, ctx->file);
778
0
        if (code != size) {
779
0
            code = gs_rethrow1(gs_error_ioerror, "failed to read %d bytes", size);
780
0
            xps_free_part(ctx, part);
781
0
            goto cleanup;
782
0
        }
783
784
0
        code = xps_parse_fixed_page(ctx, part);
785
0
        if (code)
786
0
        {
787
0
            code = gs_rethrow1(code, "cannot parse fixed page part '%s'", part->name);
788
0
            xps_free_part(ctx, part);
789
0
            goto cleanup;
790
0
        }
791
792
0
        xps_free_part(ctx, part);
793
0
        code = gs_okay;
794
0
        goto cleanup;
795
0
    }
796
797
147
    if (strstr(filename, "/_rels/.rels") || strstr(filename, "\\_rels\\.rels"))
798
0
    {
799
0
        gs_strlcpy(buf, filename, sizeof buf);
800
0
        p = strstr(buf, "/_rels/.rels");
801
0
        if (!p)
802
0
            p = strstr(buf, "\\_rels\\.rels");
803
0
        *p = 0;
804
0
        ctx->directory = xps_strdup(ctx, buf);
805
0
        if_debug1m('|', ctx->memory, "zip: using '%s' as root directory\n", ctx->directory);
806
0
    }
807
147
    else
808
147
    {
809
147
        code = xps_find_and_read_zip_dir(ctx);
810
147
        if (code < 0)
811
30
        {
812
30
            code = gs_rethrow(code, "cannot read zip central directory");
813
30
            goto cleanup;
814
30
        }
815
147
    }
816
817
117
    code = xps_read_and_process_metadata_part(ctx, "/_rels/.rels");
818
117
    if (code)
819
2
    {
820
2
        code = gs_rethrow(code, "cannot process root relationship part");
821
2
        goto cleanup;
822
2
    }
823
824
115
    if (!ctx->start_part)
825
0
    {
826
0
        code = gs_rethrow(-1, "cannot find fixed document sequence start part");
827
0
        goto cleanup;
828
0
    }
829
830
115
    code = xps_read_and_process_metadata_part(ctx, ctx->start_part);
831
115
    if (code)
832
3
    {
833
3
        code = gs_rethrow(code, "cannot process FixedDocumentSequence part");
834
3
        goto cleanup;
835
3
    }
836
837
222
    for (doc = ctx->first_fixdoc; doc; doc = doc->next)
838
112
    {
839
112
        code = xps_read_and_process_metadata_part(ctx, doc->name);
840
112
        if (code)
841
2
        {
842
2
            code = gs_rethrow(code, "cannot process FixedDocument part");
843
2
            goto cleanup;
844
2
        }
845
112
    }
846
847
    /* If we have a page list, adjust pages now */
848
110
    if (ctx->page_range && ctx->page_range->page_list)
849
0
    {
850
0
        code = xps_reorder_pages(ctx);
851
0
        if (code)
852
0
        {
853
0
            code = gs_rethrow(code, "invalid page range setting");
854
0
            goto cleanup;
855
0
        }
856
0
    }
857
858
132
    for (page = ctx->first_page; page; page = page->next)
859
110
    {
860
110
        code = xps_read_and_process_page_part(ctx, page->name);
861
110
        if (code)
862
88
        {
863
88
            code = gs_rethrow(code, "cannot process FixedPage part");
864
88
            goto cleanup;
865
88
        }
866
110
    }
867
868
22
    code = gs_okay;
869
870
147
cleanup:
871
147
    if (ctx->directory)
872
147
        xps_free(ctx, ctx->directory);
873
147
    if (ctx->file)
874
147
        xps_fclose(ctx->file);
875
147
    return code;
876
22
}