Coverage Report

Created: 2025-06-24 07:01

/src/ghostpdl/base/pagelist.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 2022-2023 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
/* Functions to parse a PageList string and processing utilitiess */
17
18
19
#include "memory_.h"
20
#include "string_.h"    /* for strcspn */
21
#include <stdlib.h>   /* for atoi */
22
#include "gsmemory.h"
23
#include "gserrors.h"
24
#include "pagelist.h"
25
26
/* array contains an "ordered" flag, then even/odd, start, end triples  */
27
28
/* Allocate an array of ranges. A range of 0-0 terminates the list  */
29
/* Note the first int 0 is order unknown, < 0 is unordered, > 0 is  */
30
/* ordered and thus is suitable for sequential parsers like PS or PCL.  */
31
/* This first int is also used by test_printed as the offset into the */
32
/* array for fast processing by SkipPage.       */
33
/* returns error code < 0 or number of ranges if success.     */
34
int
35
pagelist_parse_to_array(char *page_list, gs_memory_t *mem, int num_pages, int **parray)
36
0
{
37
0
    int i = 0, range_count = 0;
38
0
    int *pagelist_array = NULL;
39
0
    char *p = page_list;
40
0
    int prev_end = 0;   /* used to track if the ranges are ordered */
41
0
    int ordered = 1;    /* default is ordered, until it's not */
42
0
    int comma, len;
43
44
0
    *parray = NULL;     /* in case PageList is empty */
45
0
    if (page_list[0] == 0)
46
0
        return gs_error_rangecheck; /* empty list not valid */
47
48
0
    do {
49
0
        len = strlen(p);
50
0
        comma = strcspn(p, ",");
51
0
        p += comma + 1;
52
0
        if (comma > 0)
53
0
            range_count++;
54
0
    } while (comma < len);
55
0
    if (range_count == 0)
56
0
        return gs_error_rangecheck; /* not valid */
57
58
0
    range_count++;      /* room for the end of list marker: 0, 0, 0 */
59
0
    pagelist_array = (int *)gs_alloc_byte_array(mem, 1 + (range_count * 3), sizeof(int), "pagelist_parse_to_array");
60
0
    *parray = pagelist_array;   /* return the array (NULL is OK) */
61
0
    if (pagelist_array == NULL)
62
0
        return gs_error_VMerror;
63
64
0
    memset(pagelist_array, 0, (1 + range_count * 3) * sizeof(int));
65
    /* Fill the array */
66
67
0
    p = page_list;    /* go back to start of string */
68
0
    for (i=1; i < (range_count - 1) * 3; ) {
69
        /* Parsing method from Michael Vrhel's code in xps */
70
0
        int dash, start, end, even_odd;
71
72
0
        even_odd = 0;
73
0
        start = 1;
74
0
        end = -1;     /* internal flag for last page */
75
0
        len = strlen(p);
76
0
        comma = strcspn(p, ",");
77
0
        dash = strcspn(p, "-");
78
79
0
        if (comma == 0) {
80
0
            p++;
81
0
            continue;     /* skip leading or adjacent commas */
82
0
        }
83
0
        if (strncmp(p, "even", 4) == 0) {
84
0
            even_odd = 2;
85
0
            p += 4;
86
0
        } else if (strncmp(p, "odd", 3) == 0) {
87
0
            even_odd = 1;
88
0
            p += 3;
89
0
        }
90
        /* check if the next after "even" or "odd" is ':' */
91
0
        if (even_odd != 0) {
92
0
            start = even_odd;   /* default is entire range */
93
0
      end = -1;
94
0
      if (*p == ':')
95
0
    p++;     /* skip to the range */
96
0
      len = strlen(p);
97
0
      comma = strcspn(p, ",");
98
0
      dash = strcspn(p, "-");
99
0
        }
100
0
  if (comma > 0 && *p != 0) {
101
      /* We have a range to process */
102
0
      if (dash < comma) {
103
    /* Dash at start */
104
0
    if (dash == 0) {
105
0
        start = -1;
106
0
        end = atoi(p + dash + 1);
107
0
    } else {
108
0
        start = atoi(p);
109
        /* Dash at end */
110
0
        if (p[dash + 1] == 0 || p[dash + 1] == ',') {
111
0
      end = -1;
112
0
        } else {
113
0
      end = atoi(p + dash + 1);
114
0
        }
115
0
    }
116
0
      } else {
117
0
    start = atoi(p);
118
0
    end = start;
119
0
      }
120
0
  }
121
0
        if (comma == len)
122
0
            p += comma;
123
0
        else
124
0
            p += comma + 1;
125
126
        /* If even or odd, and we have a "num_pages" flag == -1, adjust to proper value */
127
0
        if (even_odd == 2) {
128
0
            if (start == -1)
129
0
                start = num_pages & -2;
130
0
            if (end == -1)
131
0
                end = num_pages & -2;
132
0
        } else if (even_odd == 1) {
133
0
            if (start == -1)
134
0
                start = num_pages - (1 & (num_pages ^ 1));
135
0
            if (end == -1)
136
0
                end = num_pages - (1 & (num_pages ^ 1));
137
0
        } else {
138
0
            if (start == -1)
139
0
                start = num_pages;
140
0
            if (end == -1)
141
0
                end = num_pages;
142
0
        }
143
        /* Store this range, bump the index as we do */
144
0
        pagelist_array[i++] = even_odd;
145
0
        pagelist_array[i++] = start;
146
0
        pagelist_array[i++] = end;
147
148
0
        if (start <= prev_end || start > end)
149
0
            ordered = -1;
150
0
        prev_end = end;
151
0
    }
152
    /* Note final array entries are 0, 0, 0 from memset above */
153
0
    pagelist_array[0] = ordered;
154
0
    return range_count;
155
0
}
156
157
/* function to calculate the total number of pages to be output   */
158
int
159
pagelist_number_of_pages(const int *parray)
160
0
{
161
0
    int count = 0, i;
162
163
    /* loop over ranges until start == 0 */
164
0
    for (i=1; parray[i+1] != 0; i += 3) {
165
0
        int start = parray[i+1];
166
0
        int end = parray[i+2];
167
168
0
        if (end >= start)
169
0
            count += 1 + end - start;
170
0
        else
171
0
            count += 1 + start - end;
172
0
    }
173
0
    return count;
174
0
}
175
176
/* Return true if this pagelist is in strict increasing order   */
177
bool
178
pagelist_test_ordered(int *parray)
179
0
{
180
0
    int prev;
181
0
    int i;
182
183
    /* check flag (fast for use by the flp SkipPage function  */
184
0
    if (parray[0] > 0)
185
0
        return true;
186
0
    if (parray[0] < 0)
187
0
        return false;
188
189
    /* parray[0] == 0, scan until start of range is 0 */
190
0
    prev = 0;     /* page 1 or more is legal */
191
0
    for (i=1; parray[i+1] != 0; i += 3) {
192
0
        if (parray[i+1] <= prev || parray[i+1] < parray[i+2])
193
0
            break;   /* NOT ordered */
194
0
        prev = parray[i+2]; /* advance to end of range */
195
0
    }
196
    /* if we made it to the marker at the end, then the list is ordered */
197
0
    parray[0] = (parray[i+1] == 0) ? 1 : -1;
198
0
    return parray[0] > 0;
199
0
}
200
201
/* Return true if this page is to be printed. (For sequential processing) */
202
/* This assumes/requires that the PageList is strictly sequential.    */
203
bool
204
pagelist_test_printed(int *parray, int pagenum)
205
0
{
206
0
    int i = parray[0];
207
208
0
    if (i <= 0) {     /* shouldn't happen */
209
0
        pagelist_test_ordered(parray);  /* in case caller didn't do this */
210
0
        i = parray[0];
211
0
        if (i < 0)
212
0
            return false;     /* list is NOT ordered, punt  */
213
0
    }
214
215
    /* advance to next range if pagenum past the end of this range  */
216
    /* stopping at the 0, 0, 0 marker (start == 0)      */
217
0
    while (pagenum > parray[i+2] && parray[i+1] != 0) {
218
0
        i += 3;     /* bump to next range for next call */
219
0
        parray[0] = i;    /* update for next entry */
220
0
    }
221
0
    if (parray[i+1] == 0)
222
0
        return false;   /* reached end of ranges */
223
224
    /* First Test even, then odd */
225
0
    if (parray[i] == 2 && (pagenum & 1) == 1)
226
0
        return false;
227
0
    if (parray[i] == 1 && (pagenum & 1) == 0)
228
0
        return false;
229
230
    /* Fast case for ordered list and sequential page numbers (FLP device) */
231
0
    if (i > 0) {
232
0
        if (pagenum >= parray[i+1] && pagenum <= parray[i+2]) {
233
0
            return true;   /* page was in this range */
234
0
        } else {
235
0
            if (pagenum < parray[i+1])
236
0
                return false; /* pagennum in between ranges that skip */
237
0
        }
238
0
    }
239
0
    return false;
240
0
}
241
242
void
243
pagelist_free_range_array(gs_memory_t *mem, int *parray)
244
0
{
245
0
    gs_free_object(mem, parray, "flp_close_device");
246
0
}