Coverage Report

Created: 2026-08-08 08:00

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