Coverage Report

Created: 2026-04-09 07:06

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
11.2k
{
35
11.2k
    int a = xps_getc(file);
36
11.2k
    int b = xps_getc(file);
37
11.2k
    return a | (b << 8);
38
11.2k
}
39
40
static inline int getlong(gp_file *file)
41
6.27k
{
42
6.27k
    unsigned int a = (unsigned int)xps_getc(file);
43
6.27k
    unsigned int b = (unsigned int)xps_getc(file);
44
6.27k
    unsigned int c = (unsigned int)xps_getc(file);
45
6.27k
    unsigned int d = (unsigned int)xps_getc(file);
46
6.27k
    return a | (b << 8) | (c << 16) | (d << 24);
47
6.27k
}
48
49
static void *
50
xps_zip_alloc_items(xps_context_t *ctx, int items, int size)
51
280
{
52
280
    void *item;
53
280
    uint32_t total;
54
55
280
    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
280
    item = xps_alloc(ctx, total);
61
280
    if (!item) {
62
0
        gs_throw(gs_error_VMerror, "out of memory: item.\n");
63
0
        return NULL;
64
0
    }
65
280
    return item;
66
280
}
67
68
static void
69
xps_zip_free(xps_context_t *ctx, void *ptr)
70
280
{
71
280
    xps_free(ctx, ptr);
72
280
}
73
74
static int
75
xps_compare_entries(const void *a0, const void *b0)
76
2.02k
{
77
2.02k
    xps_entry_t *a = (xps_entry_t*) a0;
78
2.02k
    xps_entry_t *b = (xps_entry_t*) b0;
79
2.02k
    return xps_strcasecmp(a->name, b->name);
80
2.02k
}
81
82
static xps_entry_t *
83
xps_find_zip_entry(xps_context_t *ctx, const char *name)
84
284
{
85
284
    int l = 0;
86
284
    int r = ctx->zip_count - 1;
87
987
    while (l <= r)
88
987
    {
89
987
        int m = (l + r) >> 1;
90
987
        int c = xps_strcasecmp(name, ctx->zip_table[m].name);
91
987
        if (c < 0)
92
246
            r = m - 1;
93
741
        else if (c > 0)
94
457
            l = m + 1;
95
284
        else
96
284
            return &ctx->zip_table[m];
97
987
    }
98
0
    return NULL;
99
284
}
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
284
{
108
284
    z_stream stream;
109
284
    unsigned char *inbuf;
110
284
    int sig;
111
284
    int version, general, method;
112
284
    int namelength, extralength;
113
284
    int code;
114
115
284
    if_debug1m('|', ctx->memory, "zip: inflating entry '%s'\n", ent->name);
116
117
284
    if (xps_fseek(ctx->file, ent->offset, 0) < 0)
118
0
        return gs_throw1(-1, "seek to offset %d failed.", ent->offset);
119
120
284
    sig = getlong(ctx->file);
121
284
    if (sig != ZIP_LOCAL_FILE_SIG)
122
5
        return gs_throw1(-1, "wrong zip local file signature (0x%x)", sig);
123
124
279
    version = getshort(ctx->file);
125
279
    (void)version; /* Avoid unused variable compiler warning */
126
279
    general = getshort(ctx->file);
127
279
    if (general & ZIP_ENCRYPTED_FLAG)
128
0
        return gs_throw(-1, "zip file content is encrypted");
129
279
    method = getshort(ctx->file);
130
279
    (void) getshort(ctx->file); /* file time */
131
279
    (void) getshort(ctx->file); /* file date */
132
279
    (void) getlong(ctx->file); /* crc-32 */
133
279
    (void) getlong(ctx->file); /* csize */
134
279
    (void) getlong(ctx->file); /* usize */
135
279
    namelength = getshort(ctx->file);
136
279
    extralength = getshort(ctx->file);
137
138
279
    if (namelength < 0 || namelength > 65535)
139
0
        return gs_rethrow(gs_error_ioerror, "Illegal namelength (can't happen).\n");
140
279
    if (extralength < 0 || extralength > 65535)
141
0
        return gs_rethrow(gs_error_ioerror, "Illegal extralength (can't happen).\n");
142
143
279
    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
279
    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
279
    else if (method == 8)
153
279
    {
154
279
        inbuf = xps_alloc(ctx, ent->csize);
155
279
        if (!inbuf) {
156
0
            return gs_rethrow(gs_error_VMerror, "out of memory.\n");
157
0
        }
158
159
279
        code = xps_fread(inbuf, 1, ent->csize, ctx->file);
160
279
        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
279
        memset(&stream, 0, sizeof(z_stream));
166
279
        stream.zalloc = (alloc_func) xps_zip_alloc_items;
167
279
        stream.zfree = (free_func) xps_zip_free;
168
279
        stream.opaque = ctx;
169
279
        stream.next_in = inbuf;
170
279
        stream.avail_in = ent->csize;
171
279
        stream.next_out = outbuf;
172
279
        stream.avail_out = ent->usize;
173
174
279
        code = inflateInit2(&stream, -15);
175
279
        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
279
        code = inflate(&stream, Z_FINISH);
181
279
        if (code != Z_STREAM_END)
182
2
        {
183
2
            inflateEnd(&stream);
184
2
            xps_free(ctx, inbuf);
185
2
            return gs_throw1(-1, "zlib inflate error: %s", stream.msg);
186
2
        }
187
277
        code = inflateEnd(&stream);
188
277
        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
277
        xps_free(ctx, inbuf);
195
196
        /* If the stream has less data than advertised, then zero the remainder. */
197
277
        if (stream.avail_out > 0)
198
61
        {
199
61
            gs_warn("truncated zipfile entry; possibly corrupt data");
200
61
            memset(stream.next_out, 0, stream.avail_out);
201
61
        }
202
277
    }
203
0
    else
204
0
    {
205
0
        return gs_throw1(-1, "unknown compression method (%d)", method);
206
0
    }
207
208
277
    return gs_okay;
209
279
}
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
100
{
218
100
    int sig;
219
100
    int offset, count, read;
220
100
    int namesize, metasize, commentsize;
221
100
    int i;
222
223
100
    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
100
    sig = getlong(ctx->file);
227
100
    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
100
    (void) getshort(ctx->file); /* this disk */
231
100
    (void) getshort(ctx->file); /* start disk */
232
100
    (void) getshort(ctx->file); /* entries in this disk */
233
100
    count = getshort(ctx->file); /* entries in central directory disk */
234
100
    (void) getlong(ctx->file); /* size of central directory */
235
100
    offset = getlong(ctx->file); /* offset to central directory */
236
237
100
    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
100
    ctx->zip_count = count;
241
100
    if (count > INT_MAX / sizeof(xps_entry_t *))
242
0
        return gs_rethrow(gs_error_limitcheck, "entry table too large");
243
100
    ctx->zip_table = xps_alloc(ctx, sizeof(xps_entry_t) * (size_t)count);
244
100
    if (!ctx->zip_table)
245
0
        return gs_rethrow(gs_error_VMerror, "cannot allocate zip entry table");
246
247
100
    memset(ctx->zip_table, 0, sizeof(xps_entry_t) * count);
248
249
100
    if (xps_fseek(ctx->file, offset, 0) != 0)
250
2
        return gs_throw1(gs_error_ioerror, "xps_fseek to offset %d failed", offset);
251
252
902
    for (i = 0; i < count; i++)
253
830
    {
254
830
        sig = getlong(ctx->file);
255
830
        if (sig != ZIP_CENTRAL_DIRECTORY_SIG)
256
25
            return gs_throw1(-1, "wrong zip central directory signature (0x%x)", sig);
257
258
805
        (void) getshort(ctx->file); /* version made by */
259
805
        (void) getshort(ctx->file); /* version to extract */
260
805
        (void) getshort(ctx->file); /* general */
261
805
        (void) getshort(ctx->file); /* method */
262
805
        (void) getshort(ctx->file); /* last mod file time */
263
805
        (void) getshort(ctx->file); /* last mod file date */
264
805
        (void) getlong(ctx->file); /* crc-32 */
265
805
        ctx->zip_table[i].csize = getlong(ctx->file);
266
805
        ctx->zip_table[i].usize = getlong(ctx->file);
267
805
        namesize = getshort(ctx->file);
268
805
        if (namesize < 0 || namesize > 65535)
269
0
            return gs_rethrow(gs_error_rangecheck, "illegal namesize (can't happen)");
270
805
        metasize = getshort(ctx->file);
271
805
        commentsize = getshort(ctx->file);
272
805
        (void) getshort(ctx->file); /* disk number start */
273
805
        (void) getshort(ctx->file); /* int file atts */
274
805
        (void) getlong(ctx->file); /* ext file atts */
275
805
        ctx->zip_table[i].offset = getlong(ctx->file);
276
277
805
        if (ctx->zip_table[i].csize < 0 || ctx->zip_table[i].usize < 0)
278
0
            return gs_throw(gs_error_ioerror, "cannot read zip entries larger than 2GB");
279
280
805
        if (namesize == INT_MAX)
281
0
            return gs_rethrow(gs_error_VMerror, "cannot allocate zip entry name");
282
805
        ctx->zip_table[i].name = xps_alloc(ctx, (size_t)namesize + 1);
283
805
        if (!ctx->zip_table[i].name)
284
0
            return gs_rethrow(gs_error_VMerror, "cannot allocate zip entry name");
285
286
805
        read = xps_fread(ctx->zip_table[i].name, 1, (size_t)namesize, ctx->file);
287
805
        if (read != namesize)
288
1
            return gs_throw1(gs_error_ioerror, "failed to read %d bytes", namesize);
289
290
804
        ctx->zip_table[i].name[namesize] = 0;
291
292
804
        if (xps_fseek(ctx->file, metasize, 1) != 0)
293
0
            return gs_throw1(gs_error_ioerror, "xps_fseek to offset %d failed", metasize);
294
804
        if (xps_fseek(ctx->file, commentsize, 1) != 0)
295
0
            return gs_throw1(gs_error_ioerror, "xps_fseek to offset %d failed", commentsize);
296
804
    }
297
298
72
    qsort(ctx->zip_table, count, sizeof(xps_entry_t), xps_compare_entries);
299
300
858
    for (i = 0; i < ctx->zip_count; i++)
301
786
    {
302
786
        if_debug3m('|', ctx->memory, "zip entry '%s' csize=%d usize=%d\n",
303
786
                   ctx->zip_table[i].name,
304
786
                   ctx->zip_table[i].csize,
305
786
                   ctx->zip_table[i].usize);
306
786
    }
307
308
72
    return gs_okay;
309
98
}
310
311
static int
312
xps_find_and_read_zip_dir(xps_context_t *ctx)
313
136
{
314
136
    int filesize, back, maxback;
315
136
    int i, n;
316
136
    char buf[512];
317
318
136
    if (xps_fseek(ctx->file, 0, SEEK_END) < 0)
319
0
        return gs_throw(-1, "seek to end failed.");
320
321
136
    filesize = xps_ftell(ctx->file);
322
323
136
    maxback = MIN(filesize, 0xFFFF + sizeof buf);
324
136
    back = MIN(maxback, sizeof buf);
325
326
1.08k
    while (back < maxback)
327
1.04k
    {
328
1.04k
        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
1.04k
        n = xps_fread(buf, 1, sizeof buf, ctx->file);
332
1.04k
        if (n < 0)
333
0
            return gs_throw(-1, "cannot read end of central directory");
334
335
485k
        for (i = n - 4; i > 0; i--)
336
484k
            if (!memcmp(buf + i, "PK\5\6", 4))
337
100
                return xps_read_zip_dir(ctx, filesize - back + i);
338
339
946
        back += sizeof buf - 4;
340
946
    }
341
342
36
    return gs_throw(-1, "cannot find end of central directory");
343
136
}
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
284
{
352
284
    char buf[2048];
353
284
    xps_entry_t *ent;
354
284
    xps_part_t *part;
355
284
    int count, size, offset, i;
356
284
    int code = 0;
357
284
    const char *name;
358
284
    int seen_last = 0;
359
360
284
    name = partname;
361
284
    if (name[0] == '/')
362
284
        name ++;
363
364
    /* All in one piece */
365
284
    ent = xps_find_zip_entry(ctx, name);
366
284
    if (ent)
367
284
    {
368
284
        part = xps_new_part(ctx, partname, ent->usize);
369
284
        if (part != NULL)
370
284
            code = xps_read_zip_entry(ctx, ent, part->data);
371
0
        else
372
0
            return NULL;
373
284
        if (code < 0)
374
7
        {
375
7
            xps_free_part(ctx, part);
376
7
            gs_rethrow1(-1, "cannot read zip entry '%s'", name);
377
7
            return NULL;
378
7
        }
379
277
        return part;
380
284
    }
381
382
    /* Count the number of pieces and their total size */
383
0
    count = 0;
384
0
    size = 0;
385
0
    while (!seen_last)
386
0
    {
387
0
        gs_snprintf(buf, sizeof(buf), "%s/[%d].piece", name, count);
388
0
        ent = xps_find_zip_entry(ctx, buf);
389
0
        if (!ent)
390
0
        {
391
0
            gs_snprintf(buf, sizeof(buf), "%s/[%d].last.piece", name, count);
392
0
            ent = xps_find_zip_entry(ctx, buf);
393
0
            seen_last = !!ent;
394
0
        }
395
0
        if (!ent)
396
0
            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
0
    if (!seen_last)
407
0
    {
408
0
        gs_throw1(-1, "cannot find all pieces for part '%s'", partname);
409
0
        return NULL;
410
0
    }
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
284
{
544
284
    if (ctx->directory)
545
0
        return xps_read_dir_part(ctx, partname);
546
284
    return xps_read_zip_part(ctx, partname);
547
284
}
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
212
{
556
212
    xps_part_t *part;
557
212
    int code;
558
559
212
    part = xps_read_part(ctx, name);
560
212
    if (!part)
561
3
        return gs_rethrow1(-1, "cannot read zip part '%s'", name);
562
563
209
    code = xps_parse_metadata(ctx, part);
564
209
    if (code)
565
1
    {
566
1
        xps_free_part(ctx, part);
567
1
        return gs_rethrow1(code, "cannot process metadata part '%s'", name);
568
1
    }
569
570
208
    xps_free_part(ctx, part);
571
572
208
    return gs_okay;
573
209
}
574
575
static int
576
xps_read_and_process_page_part(xps_context_t *ctx, char *name)
577
68
{
578
68
    xps_part_t *part;
579
68
    int code;
580
581
68
    part = xps_read_part(ctx, name);
582
68
    if (!part)
583
3
        return gs_rethrow1(-1, "cannot read zip part '%s'", name);
584
585
65
    code = xps_parse_fixed_page(ctx, part);
586
65
    if (code)
587
61
    {
588
61
        xps_free_part(ctx, part);
589
61
        return gs_rethrow1(code, "cannot parse fixed page part '%s'", name);
590
61
    }
591
592
4
    xps_free_part(ctx, part);
593
594
4
    return gs_okay;
595
65
}
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
136
{
719
136
    char buf[2048];
720
136
    xps_document_t *doc;
721
136
    xps_page_t *page;
722
136
    int code;
723
136
    char *p;
724
725
136
    ctx->file = xps_fopen(ctx->memory, filename, "rb");
726
136
    if (!ctx->file)
727
0
        return gs_throw1(-1, "cannot open file: '%s'", filename);
728
729
136
    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
136
    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
136
    else
808
136
    {
809
136
        code = xps_find_and_read_zip_dir(ctx);
810
136
        if (code < 0)
811
64
        {
812
64
            code = gs_rethrow(code, "cannot read zip central directory");
813
64
            goto cleanup;
814
64
        }
815
136
    }
816
817
72
    code = xps_read_and_process_metadata_part(ctx, "/_rels/.rels");
818
72
    if (code)
819
1
    {
820
1
        code = gs_rethrow(code, "cannot process root relationship part");
821
1
        goto cleanup;
822
1
    }
823
824
71
    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
71
    code = xps_read_and_process_metadata_part(ctx, ctx->start_part);
831
71
    if (code)
832
2
    {
833
2
        code = gs_rethrow(code, "cannot process FixedDocumentSequence part");
834
2
        goto cleanup;
835
2
    }
836
837
137
    for (doc = ctx->first_fixdoc; doc; doc = doc->next)
838
69
    {
839
69
        code = xps_read_and_process_metadata_part(ctx, doc->name);
840
69
        if (code)
841
1
        {
842
1
            code = gs_rethrow(code, "cannot process FixedDocument part");
843
1
            goto cleanup;
844
1
        }
845
69
    }
846
847
    /* If we have a page list, adjust pages now */
848
68
    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
72
    for (page = ctx->first_page; page; page = page->next)
859
68
    {
860
68
        code = xps_read_and_process_page_part(ctx, page->name);
861
68
        if (code)
862
64
        {
863
64
            code = gs_rethrow(code, "cannot process FixedPage part");
864
64
            goto cleanup;
865
64
        }
866
68
    }
867
868
4
    code = gs_okay;
869
870
136
cleanup:
871
136
    if (ctx->directory)
872
136
        xps_free(ctx, ctx->directory);
873
136
    if (ctx->file)
874
136
        xps_fclose(ctx->file);
875
136
    return code;
876
4
}