/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 | } |