/src/ghostpdl/gpdl/psitop.c
Line | Count | Source |
1 | | /* Copyright (C) 2001-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 | | /* psitop.c */ |
17 | | /* Top-level API implementation of PS Language Interface */ |
18 | | |
19 | | #include "stdio_.h" |
20 | | #include "ghost.h" |
21 | | #include "imain.h" |
22 | | #include "imainarg.h" |
23 | | #include "iapi.h" |
24 | | #include "psapi.h" |
25 | | #include "string_.h" |
26 | | #include "gdebug.h" |
27 | | #include "gp.h" |
28 | | #include "gserrors.h" |
29 | | #include "gstypes.h" |
30 | | #include "gsmemory.h" |
31 | | #include "gsmalloc.h" |
32 | | #include "gsstate.h" /* must precede gsdevice.h */ |
33 | | #include "gxdevice.h" /* must precede gsdevice.h */ |
34 | | #include "gsdevice.h" |
35 | | #include "icstate.h" /* for i_ctx_t */ |
36 | | #include "iminst.h" |
37 | | #include "gsstruct.h" /* for gxalloc.h */ |
38 | | #include "gspaint.h" |
39 | | #include "gxalloc.h" |
40 | | #include "gxstate.h" |
41 | | #include "plparse.h" |
42 | | #include "pltop.h" |
43 | | #include "plmain.h" |
44 | | #include "gzstate.h" |
45 | | #include "gsicc_manage.h" |
46 | | |
47 | | /* Forward decls */ |
48 | | |
49 | | /************************************************************/ |
50 | | /******** Language wrapper implementation (see pltop.h) *****/ |
51 | | /************************************************************/ |
52 | | |
53 | | /* Import operator procedures */ |
54 | | extern int zflush(i_ctx_t *); |
55 | | |
56 | | /* |
57 | | * PS interpreter instance: derived from pl_interp_implementation_t |
58 | | */ |
59 | | typedef struct ps_interp_instance_s { |
60 | | gs_memory_t *memory; |
61 | | uint bytes_fed; |
62 | | gs_lib_ctx_t *psapi_instance; |
63 | | } ps_interp_instance_t; |
64 | | |
65 | | static int |
66 | | check_token(int token_type, const char *s, const char *e, int *score) |
67 | 2.16M | { |
68 | | /* Empty tokens are always OK */ |
69 | 2.16M | if (s == e) |
70 | 1.70M | return 0; |
71 | | |
72 | 458k | switch (token_type) { |
73 | 1.77k | case 'a': |
74 | | /* Angle bracket - mainly to catch << */ |
75 | 1.77k | break; |
76 | 2.12k | case 'n': |
77 | | /* Name */ |
78 | | /* FIXME: Check it's a valid name */ |
79 | 2.12k | break; |
80 | 122 | case 'd': |
81 | | /* Dictionary */ |
82 | 122 | break; |
83 | 49.1k | case 'i': |
84 | | /* int - ok by construction. */ |
85 | 49.1k | return 0; |
86 | 7.40k | case 'f': |
87 | | /* float - ok by construction. */ |
88 | 7.40k | return 0; |
89 | 458k | } |
90 | | |
91 | 6.43M | #define TOKEN_CHECK(n) else if (e-s == strlen(n) && memcmp(s, n, e-s) == 0) { score[0] += e-s; score[1]++; } |
92 | | |
93 | 402k | if (0) { /* Initial block of checking chain */ } |
94 | 402k | TOKEN_CHECK("dup") |
95 | 402k | TOKEN_CHECK("exch") |
96 | 402k | TOKEN_CHECK("grestore") |
97 | 402k | TOKEN_CHECK("gsave") |
98 | 402k | TOKEN_CHECK("idiv") |
99 | 402k | TOKEN_CHECK("lineto") |
100 | 402k | TOKEN_CHECK("mod") |
101 | 402k | TOKEN_CHECK("mul") |
102 | 402k | TOKEN_CHECK("moveto") |
103 | 402k | TOKEN_CHECK("setflat") |
104 | 402k | TOKEN_CHECK("setlinecap") |
105 | 402k | TOKEN_CHECK("setlinejoin") |
106 | 402k | TOKEN_CHECK("showpage") |
107 | 402k | TOKEN_CHECK("stroke") |
108 | 402k | TOKEN_CHECK("translate") |
109 | 402k | TOKEN_CHECK("systemdict") |
110 | | |
111 | 402k | if (score[0] > 1024 && score[1] >= 3) |
112 | 0 | return 1; |
113 | 402k | if (score[0] < -1024) |
114 | 541 | return 1; |
115 | | |
116 | 401k | return 0; |
117 | 402k | } |
118 | | |
119 | | static int |
120 | | score_comment(const char *s, const char *e, int *score) |
121 | 5.11k | { |
122 | 66.3k | #define COMMENT_CHECK(n) else if (e-s >= strlen(n) && memcmp(s, n, strlen(n)) == 0) { score[0] += 100; score[1]++; } |
123 | | |
124 | 5.11k | if (0) { /* Initial block of checking chain */ } |
125 | 5.11k | COMMENT_CHECK("!PS") |
126 | 5.11k | COMMENT_CHECK("%Title:") |
127 | 5.10k | COMMENT_CHECK("%Version:") |
128 | 5.10k | COMMENT_CHECK("%Creator:") |
129 | 5.10k | COMMENT_CHECK("%CreationDate:") |
130 | 5.10k | COMMENT_CHECK("%Document") |
131 | 5.10k | COMMENT_CHECK("%BoundingBox:") |
132 | 5.10k | COMMENT_CHECK("%HiResBoundingBox:") |
133 | 5.10k | COMMENT_CHECK("%Pages:") |
134 | 5.10k | COMMENT_CHECK("%+ procset") |
135 | 5.10k | COMMENT_CHECK("%End") |
136 | 5.10k | COMMENT_CHECK("%Begin") |
137 | 5.10k | COMMENT_CHECK("%Copyright") |
138 | 5.10k | else { |
139 | 5.10k | score[0]++; score[1]++; |
140 | 5.10k | } |
141 | | |
142 | 5.11k | if (score[0] > 1024 && score[1] >= 3) |
143 | 0 | return 1; |
144 | | |
145 | 5.11k | return 0; |
146 | 5.11k | } |
147 | | |
148 | | static int |
149 | | ps_detect_language(const char *s, int len) |
150 | 17.7k | { |
151 | | /* For postscript, we look for %! */ |
152 | 17.7k | if (len >= 2) { |
153 | 17.7k | if (s[0] != '%' || s[1] != '!') { |
154 | | /* Not what we were looking for */ |
155 | 17.7k | } else if (len >= 12 && memcmp(s+2, "Postscript", 10) == 0) { |
156 | 0 | return 100; |
157 | 2 | } else if (len >= 4 && memcmp(s+2, "PS", 2) == 0) { |
158 | 0 | return 100; |
159 | 2 | } else if (len >= 3 && s[2] == '/') { |
160 | | /* Looks like a shell script. Don't want that. */ |
161 | 0 | return 0; |
162 | 2 | } else { |
163 | | /* If it begins %!, then it's PROBABLY postscript */ |
164 | 2 | return 80; |
165 | 2 | } |
166 | 17.7k | } |
167 | | /* For PDF, we allow for leading crap, then a postscript version marker */ |
168 | 17.7k | { |
169 | 17.7k | const char *t = s; |
170 | 17.7k | int left = len-22; |
171 | | /* Search within just the first 4K, plus a bit for the marker length. */ |
172 | 17.7k | if (left > 4096+22) |
173 | 0 | left = 4096+22; |
174 | 21.9M | while (left > 22) { |
175 | 21.8M | if (memcmp(t, "%PDF-", 5) == 0 && |
176 | 66 | t[5] >= '1' && t[5] <= '9' && |
177 | 38 | t[6] == '.' && |
178 | 2 | t[7] >= '0' && t[7] <= '9') { |
179 | 2 | return 100; |
180 | 2 | } |
181 | 21.8M | if (memcmp(t, "%!PS-Adobe-", 11) == 0 && |
182 | 6 | t[11] >= '0' && t[11] <= '9' && |
183 | 0 | t[12] == '.' && |
184 | 0 | t[13] >= '0' && t[13] <= '9' && |
185 | 0 | memcmp(t+14, " PDF-", 5) == 0 && |
186 | 0 | t[19] >= '0' && t[19] <= '9' && |
187 | 0 | t[20] == '.' && |
188 | 0 | t[21] >= '0' && t[21] <= '9') { |
189 | 0 | return 100; |
190 | 0 | } |
191 | 21.8M | t++; |
192 | 21.8M | left--; |
193 | 21.8M | } |
194 | 17.7k | } |
195 | | |
196 | | /* Now we do some seriously hairy stuff */ |
197 | 17.7k | { |
198 | 17.7k | const char *t = s; |
199 | 17.7k | const char *token = t; |
200 | 17.7k | int left = len; |
201 | 17.7k | int token_type = 0; |
202 | 17.7k | int score[2] = { 0, 0 }; |
203 | | |
204 | 3.39M | while (left--) { |
205 | 3.38M | if (*t == '%') { |
206 | 5.11k | if (check_token(token_type, token, t, score)) |
207 | 1 | break; |
208 | | /* Skip to end of line */ |
209 | 5.11k | left--; |
210 | 5.11k | token = ++t; |
211 | 1.22M | while (left && *t != '\r' && *t != '\n') { |
212 | 1.21M | left--; t++; |
213 | 1.21M | } |
214 | 5.11k | if (score_comment(token, t, score)) |
215 | 0 | break; |
216 | | /* Skip any combination of newlines */ |
217 | 9.86k | while (left && (*t == '\r' || *t == '\n')) { |
218 | 4.75k | left--; t++; |
219 | 4.75k | } |
220 | 5.11k | token_type = 0; |
221 | 5.11k | continue; |
222 | 3.38M | } else if (*t == 27) { |
223 | | /* Give up if we meet an ESC. It could be a UEL. */ |
224 | 12.0k | break; |
225 | 3.37M | } else if (*t <= 32 || *(unsigned char *)t > 127) { |
226 | 2.05M | if (check_token(token_type, token, t, score)) |
227 | 506 | break; |
228 | 2.05M | if (*t != 9 && *t != 10 && *t != 12 && *t != 13 && *t != 32) |
229 | 1.85M | score[0]--; |
230 | 2.05M | token = t+1; |
231 | 2.05M | token_type = 0; |
232 | 2.05M | } else if (*t == '/') { |
233 | 9.37k | if (check_token(token_type, token, t, score)) |
234 | 4 | break; |
235 | 9.37k | token = t+1; |
236 | 9.37k | token_type = 'n'; |
237 | 1.30M | } else if (*t == '[' || *t == ']' || *t == '{' || *t == '}') { |
238 | 28.3k | if (check_token(token_type, token, t, score)) |
239 | 5 | break; |
240 | 28.3k | token = t+1; |
241 | 28.3k | token_type = 0; |
242 | 1.28M | } else if (*t == '<') { |
243 | 9.41k | if (token_type == 'a') { |
244 | | /* << */ |
245 | 173 | token = t+1; |
246 | 173 | token_type = 'd'; |
247 | 9.24k | } else if (token_type == 'd') { |
248 | | /* <<< ???!? */ |
249 | 1.12k | score[0] -= 10; |
250 | 8.11k | } else { |
251 | 8.11k | if (check_token(token_type, token, t, score)) |
252 | 1 | break; |
253 | 8.11k | token = t+1; |
254 | 8.11k | token_type = 'a'; |
255 | 8.11k | } |
256 | 1.27M | } else if (*t == '>') { |
257 | 2.92k | if (check_token(token_type, token, t, score)) |
258 | 4 | break; |
259 | 2.92k | token = t+1; |
260 | 2.92k | token_type = 0; |
261 | 1.26M | } else if (*t >= '0' && *t <= '9') { |
262 | 154k | if (token_type == 'i') { |
263 | | /* Keep going */ |
264 | 90.8k | } else if (token_type == 'f') { |
265 | | /* Keep going */ |
266 | 55.3k | } else { |
267 | 55.3k | if (check_token(token_type, token, t, score)) |
268 | 16 | break; |
269 | 55.3k | token = t; |
270 | 55.3k | token_type = 'i'; |
271 | 55.3k | } |
272 | 1.11M | } else if (*t == '.') { |
273 | 11.8k | if (token_type == 'f') { |
274 | | /* seems unlikely */ |
275 | 2.05k | score[0]--; |
276 | 2.05k | break; |
277 | 9.79k | } else if (token_type == 'i') { |
278 | 5.64k | token = t; |
279 | 5.64k | token_type = 'f'; |
280 | 5.64k | } else { |
281 | 4.14k | if (check_token(token_type, token, t, score)) |
282 | 4 | break; |
283 | 4.14k | token = t; |
284 | 4.14k | token_type = 'f'; |
285 | 4.14k | } |
286 | 1.10M | } else { |
287 | | /* Assume anything else goes into the token */ |
288 | 1.10M | } |
289 | 3.36M | t++; |
290 | 3.36M | } |
291 | 17.7k | if (score[0] < 0 || score[1] < 3) |
292 | 17.7k | return 0; /* Unlikely to be PS */ |
293 | 7 | else if (score[0] > 0) |
294 | 7 | return 75; /* Could be PS */ |
295 | 17.7k | } |
296 | | |
297 | 0 | return 0; |
298 | 17.7k | } |
299 | | |
300 | | /* Get implementation's characteristics */ |
301 | | static const pl_interp_characteristics_t * /* always returns a descriptor */ |
302 | | ps_impl_characteristics(const pl_interp_implementation_t *impl) /* implementation of interpreter to alloc */ |
303 | 37.5k | { |
304 | | /* version and build date are not currently used */ |
305 | 37.5k | static const pl_interp_characteristics_t ps_characteristics = { |
306 | 37.5k | "POSTSCRIPT", |
307 | 37.5k | ps_detect_language, |
308 | 37.5k | }; |
309 | | |
310 | 37.5k | return &ps_characteristics; |
311 | 37.5k | } |
312 | | |
313 | | /* Do per-instance interpreter allocation/init. */ |
314 | | static int |
315 | | ps_impl_allocate_interp_instance(pl_interp_implementation_t *impl, gs_memory_t *mem) |
316 | 8.09k | { |
317 | 8.09k | ps_interp_instance_t *psi |
318 | 8.09k | = (ps_interp_instance_t *)gs_alloc_bytes( mem, |
319 | 8.09k | sizeof(ps_interp_instance_t), |
320 | 8.09k | "ps_impl_allocate_interp_instance"); |
321 | | |
322 | 8.09k | int code; |
323 | 8.09k | #define GS_MAX_NUM_ARGS 10 |
324 | 8.09k | const char *gsargs[GS_MAX_NUM_ARGS] = {0}; |
325 | 8.09k | int nargs = 0; |
326 | | |
327 | 8.09k | if (!psi) |
328 | 0 | return gs_error_VMerror; |
329 | | |
330 | 8.09k | gsargs[nargs++] = "gpdl"; |
331 | | /* We start gs with the nullpage device, and replace the device with the |
332 | | * set_device call from the language independent code. |
333 | | */ |
334 | 8.09k | gsargs[nargs++] = "-sDEVICE=nullpage"; |
335 | | /* As we're "printer targetted, use a jobserver */ |
336 | 8.09k | gsargs[nargs++] = "-dJOBSERVER"; |
337 | | |
338 | 8.09k | psi->memory = mem; |
339 | 8.09k | psi->bytes_fed = 0; |
340 | 8.09k | psi->psapi_instance = gs_lib_ctx_get_interp_instance(mem); |
341 | 8.09k | code = psapi_new_instance(&psi->psapi_instance, NULL); |
342 | 8.09k | if (code < 0) { |
343 | 0 | gs_free_object(mem, psi, "ps_impl_allocate_interp_instance"); |
344 | 0 | return code; |
345 | 0 | } |
346 | | |
347 | | /* The above call to psapi_new_instance will have set the ps interpreter |
348 | | * to expect 'local' encoding. When running under PL, this means we'll |
349 | | * end up decoding the input stuff to utf8, and then feed that into the |
350 | | * gs instance, where it will decode it again! Avoid this, by setting |
351 | | * gs to expect UTF8 input. */ |
352 | 8.09k | psapi_set_arg_encoding(psi->psapi_instance, PS_ARG_ENCODING_UTF8); |
353 | | |
354 | 8.09k | impl->interp_client_data = psi; |
355 | | |
356 | | /* Tell gs not to ignore a UEL, but do an interpreter exit */ |
357 | 8.09k | psapi_act_on_uel(psi->psapi_instance); |
358 | | |
359 | 8.09k | code = psapi_init_with_args01(psi->psapi_instance, nargs, (char **)gsargs); |
360 | 8.09k | if (code < 0) { |
361 | 0 | (void)psapi_exit(psi->psapi_instance); |
362 | 0 | psapi_delete_instance(psi->psapi_instance); |
363 | 0 | gs_free_object(mem, psi, "ps_impl_allocate_interp_instance"); |
364 | 0 | } |
365 | 8.09k | return code; |
366 | 8.09k | } |
367 | | |
368 | | /* |
369 | | * Get the allocator with which to allocate a device |
370 | | */ |
371 | | static gs_memory_t * |
372 | | ps_impl_get_device_memory(pl_interp_implementation_t *impl) |
373 | 8.09k | { |
374 | 8.09k | ps_interp_instance_t *psi = (ps_interp_instance_t *)impl->interp_client_data; |
375 | | |
376 | 8.09k | return psapi_get_device_memory(psi->psapi_instance); |
377 | 8.09k | } |
378 | | |
379 | | static int |
380 | | ps_impl_set_param(pl_interp_implementation_t *impl, |
381 | | gs_param_list *plist) |
382 | 32.3k | { |
383 | 32.3k | ps_interp_instance_t *psi = (ps_interp_instance_t *)impl->interp_client_data; |
384 | | |
385 | 32.3k | return psapi_set_param(psi->psapi_instance, plist); |
386 | 32.3k | } |
387 | | |
388 | | static int |
389 | | ps_impl_add_path(pl_interp_implementation_t *impl, |
390 | | const char *path) |
391 | 0 | { |
392 | 0 | ps_interp_instance_t *psi = (ps_interp_instance_t *)impl->interp_client_data; |
393 | |
|
394 | 0 | return psapi_add_path(psi->psapi_instance, path); |
395 | 0 | } |
396 | | |
397 | | static int |
398 | | ps_impl_post_args_init(pl_interp_implementation_t *impl) |
399 | 8.09k | { |
400 | 8.09k | ps_interp_instance_t *psi = (ps_interp_instance_t *)impl->interp_client_data; |
401 | 8.09k | const float *resolutions; |
402 | 8.09k | const long *page_sizes; |
403 | | |
404 | 8.09k | pl_main_get_forced_geometry(psi->memory, &resolutions, &page_sizes); |
405 | 8.09k | psapi_force_geometry(psi->psapi_instance, resolutions, page_sizes); |
406 | | |
407 | 8.09k | return psapi_init_with_args2(psi->psapi_instance); |
408 | 8.09k | } |
409 | | |
410 | | /* Prepare interp instance for the next "job" */ |
411 | | static int |
412 | | ps_impl_init_job(pl_interp_implementation_t *impl, |
413 | | gx_device *device) |
414 | 3 | { |
415 | 3 | ps_interp_instance_t *psi = (ps_interp_instance_t *)impl->interp_client_data; |
416 | 3 | int exit_code; |
417 | 3 | int code; |
418 | | |
419 | | /* Any error after here *must* reset the device to null */ |
420 | 3 | code = psapi_set_device(psi->psapi_instance, device); |
421 | | |
422 | 3 | if (code >= 0) |
423 | 3 | code = psapi_run_string(psi->psapi_instance, "erasepage", 0, &exit_code); |
424 | | |
425 | 3 | if (code < 0) { |
426 | 0 | int code1 = psapi_set_device(psi->psapi_instance, NULL); |
427 | 0 | (void)code1; |
428 | 0 | } |
429 | | |
430 | | /* Make sure the PageSpotColors is set to -1 for PS */ |
431 | 3 | { |
432 | 3 | gs_c_param_list* params; |
433 | 3 | int page_spot_colors = -1; |
434 | 3 | int code2; |
435 | | |
436 | 3 | params = gs_c_param_list_alloc(psi->memory, "ps_impl_init_job"); |
437 | 3 | if (params == NULL) |
438 | 0 | return_error(gs_error_VMerror); |
439 | | |
440 | 3 | gs_c_param_list_write(params, psi->memory); |
441 | 3 | gs_param_list_set_persistent_keys((gs_param_list*)params, false); |
442 | | |
443 | 3 | code2 = param_write_int((gs_param_list*)params, "PageSpotColors", &(page_spot_colors)); |
444 | 3 | if (code2 < 0) { |
445 | 0 | gs_c_param_list_free(psi->memory, params, "ps_impl_init_job"); |
446 | 0 | return code2; |
447 | 0 | } |
448 | | |
449 | 3 | gs_c_param_list_read(params); |
450 | | |
451 | 3 | code2 = psapi_set_device_param(psi->psapi_instance, (gs_param_list*)params); |
452 | 3 | gs_c_param_list_free(psi->memory, params, "ps_impl_init_job"); |
453 | 3 | if (code2 < 0) |
454 | 0 | return code2; |
455 | 3 | } |
456 | 3 | return code; |
457 | 3 | } |
458 | | |
459 | | static int |
460 | | ps_impl_run_prefix_commands(pl_interp_implementation_t *impl, |
461 | | const char *prefix) |
462 | 0 | { |
463 | 0 | ps_interp_instance_t *psi = (ps_interp_instance_t *)impl->interp_client_data; |
464 | 0 | int exit_code; |
465 | 0 | int code = 0; |
466 | |
|
467 | 0 | if (prefix == NULL) |
468 | 0 | return 0; |
469 | | |
470 | | /* Any error after here *must* reset the device to null */ |
471 | 0 | code = psapi_run_string(psi->psapi_instance, prefix, 0, &exit_code); |
472 | |
|
473 | 0 | if (code < 0) { |
474 | 0 | int code1 = psapi_set_device(psi->psapi_instance, NULL); |
475 | 0 | (void)code1; |
476 | 0 | } |
477 | |
|
478 | 0 | return code; |
479 | 0 | } |
480 | | |
481 | | static int |
482 | | ps_impl_process_file(pl_interp_implementation_t *impl, const char *filename) |
483 | 2 | { |
484 | 2 | ps_interp_instance_t *psi = (ps_interp_instance_t *)impl->interp_client_data; |
485 | 2 | int exit_code; |
486 | | |
487 | 2 | return psapi_run_file(psi->psapi_instance, filename, 0, &exit_code); |
488 | 2 | } |
489 | | |
490 | | /* Do any setup for parser per-cursor */ |
491 | | static int /* ret 0 or +ve if ok, else -ve error code */ |
492 | | ps_impl_process_begin(pl_interp_implementation_t * impl) |
493 | 1 | { |
494 | 1 | ps_interp_instance_t *psi = (ps_interp_instance_t *)impl->interp_client_data; |
495 | 1 | int exit_code; |
496 | | |
497 | 1 | psi->bytes_fed = 0; |
498 | 1 | return psapi_run_string_begin(psi->psapi_instance, 0, &exit_code); |
499 | 1 | } |
500 | | |
501 | | /* TODO: in some fashion have gs pass back how far into the input buffer it |
502 | | * had read, so we don't need to explicitly search the buffer for the UEL |
503 | | */ |
504 | | static int |
505 | | ps_impl_process(pl_interp_implementation_t * impl, stream_cursor_read * pr) |
506 | 1 | { |
507 | 1 | ps_interp_instance_t *psi = (ps_interp_instance_t *)impl->interp_client_data; |
508 | 1 | unsigned int len; |
509 | 1 | int code, exit_code = 0; |
510 | | |
511 | 1 | if (psi->bytes_fed == 0) |
512 | 1 | { |
513 | | /* Skip over whitespace/comments looking for a PDF marker. */ |
514 | 7 | while (pr->ptr < pr->limit) |
515 | 7 | { |
516 | 7 | int i; |
517 | | |
518 | | /* Skip over whitespace (as defined in PLRM) */ |
519 | 7 | if (pr->ptr[1] == 0 || |
520 | 7 | pr->ptr[1] == 9 || |
521 | 7 | pr->ptr[1] == 10 || |
522 | 7 | pr->ptr[1] == 12 || |
523 | 7 | pr->ptr[1] == 13 || |
524 | 7 | pr->ptr[1] == 32) { |
525 | 6 | pr->ptr++; |
526 | 6 | continue; |
527 | 6 | } |
528 | | |
529 | | /* If we're not starting a comment, exit. */ |
530 | 1 | if (pr->ptr[1] != '%') |
531 | 1 | break; |
532 | | |
533 | | /* If we're starting with a PDF header, swap to file mode. */ |
534 | 0 | if (pr->limit - pr->ptr >= 8 && |
535 | 0 | strncmp((const char *)&pr->ptr[2], "PDF-", 4) == 0 && |
536 | 0 | (pr->ptr[6] >= '1' && pr->ptr[6] <= '9') && |
537 | 0 | pr->ptr[7] == '.' && |
538 | 0 | (pr->ptr[8] >= '0' && pr->ptr[8] <= '9')) |
539 | 0 | return_error(gs_error_NeedFile); |
540 | | |
541 | | /* Check for a historical PDF header. */ |
542 | 0 | if (pr->limit - pr->ptr >= 22 && |
543 | 0 | strncmp((const char *)&pr->ptr[2], "!PS-Adobe-", 10) == 0 && |
544 | 0 | (pr->ptr[12] >= '0' && pr->ptr[12] <= '9') && |
545 | 0 | pr->ptr[13] == '.' && |
546 | 0 | (pr->ptr[14] >= '0' && pr->ptr[14] <= '9') && |
547 | 0 | strncmp((const char *)&pr->ptr[15], " PDF-", 5) == 0 && |
548 | 0 | (pr->ptr[20] >= '0' && pr->ptr[20] <= '9') && |
549 | 0 | pr->ptr[21] == '.' && |
550 | 0 | (pr->ptr[22] >= '0' && pr->ptr[22] <= '9')) |
551 | 0 | return_error(gs_error_NeedFile); |
552 | | |
553 | | /* Do we have a complete comment that we can skip? */ |
554 | 0 | for (i = 1; pr->ptr + i < pr->limit; i++) |
555 | 0 | if (pr->ptr[i+1] == 10 || pr->ptr[i+1] == 13) { |
556 | 0 | pr->ptr += i; |
557 | 0 | i = 0; /* Loop again in case there are more comments. */ |
558 | 0 | break; |
559 | 0 | } |
560 | | /* If we fall out of the loop naturally, then we hit the end |
561 | | * of the buffer without terminating our comment. We need to |
562 | | * abort the loop and return. */ |
563 | 0 | if (i != 0) |
564 | 0 | return_error(gs_error_NeedInput); |
565 | 0 | } |
566 | 1 | } |
567 | | |
568 | 1 | len = pr->limit - pr->ptr; |
569 | 1 | code = psapi_run_string_continue(psi->psapi_instance, (const char *)pr->ptr + 1, len, 0, &exit_code); |
570 | 1 | if (exit_code == gs_error_InterpreterExit) { |
571 | 0 | int64_t offset; |
572 | |
|
573 | 0 | offset = psapi_get_uel_offset(psi->psapi_instance) - psi->bytes_fed; |
574 | 0 | pr->ptr += offset; |
575 | 0 | psi->bytes_fed += offset + 1; |
576 | |
|
577 | | #ifdef SEND_CTRLD_AFTER_UEL |
578 | | { |
579 | | const char eot[1] = {4}; |
580 | | code = psapi_run_string_continue(psi->psapi_instance, eot, 1, 0, &exit_code); |
581 | | (void)code; /* Ignoring code here */ |
582 | | } |
583 | | #endif |
584 | 0 | return gs_error_InterpreterExit; |
585 | 0 | } |
586 | 1 | else { |
587 | 1 | pr->ptr = pr->limit; |
588 | 1 | psi->bytes_fed += len; |
589 | 1 | } |
590 | 1 | return code; |
591 | 1 | } |
592 | | |
593 | | static int /* ret 0 or +ve if ok, else -ve error code */ |
594 | | ps_impl_process_end(pl_interp_implementation_t * impl) |
595 | 1 | { |
596 | 1 | ps_interp_instance_t *psi = (ps_interp_instance_t *)impl->interp_client_data; |
597 | 1 | int exit_code, code; |
598 | | |
599 | 1 | code = psapi_run_string_end(psi->psapi_instance, 0, &exit_code); |
600 | | |
601 | 1 | if (exit_code == gs_error_InterpreterExit || code == gs_error_NeedInput) |
602 | 0 | code = 0; |
603 | | |
604 | 1 | return code; |
605 | 1 | } |
606 | | |
607 | | /* Not implemented */ |
608 | | static int |
609 | | ps_impl_flush_to_eoj(pl_interp_implementation_t *impl, stream_cursor_read *cursor) |
610 | 1 | { |
611 | 1 | const byte *p = cursor->ptr; |
612 | 1 | const byte *rlimit = cursor->limit; |
613 | | |
614 | | /* Skip to, but leave UEL in buffer for PJL to find later */ |
615 | 1 | for (; p < rlimit; ++p) |
616 | 0 | if (p[1] == '\033') { |
617 | 0 | uint avail = rlimit - p; |
618 | |
|
619 | 0 | if (memcmp(p + 1, "\033%-12345X", min(avail, 9))) |
620 | 0 | continue; |
621 | 0 | if (avail < 9) |
622 | 0 | break; |
623 | 0 | cursor->ptr = p; |
624 | 0 | return 1; /* found eoj */ |
625 | 0 | } |
626 | 1 | cursor->ptr = p; |
627 | 1 | return 0; /* need more */ |
628 | 1 | } |
629 | | |
630 | | /* Parser action for end-of-file */ |
631 | | static int |
632 | | ps_impl_process_eof(pl_interp_implementation_t *impl) |
633 | 0 | { |
634 | 0 | return 0; |
635 | 0 | } |
636 | | |
637 | | /* Report any errors after running a job */ |
638 | | static int |
639 | | ps_impl_report_errors(pl_interp_implementation_t *impl, /* interp instance to wrap up job in */ |
640 | | int code, /* prev termination status */ |
641 | | long file_position, /* file position of error, -1 if unknown */ |
642 | | bool force_to_cout /* force errors to cout */ |
643 | | ) |
644 | 1 | { |
645 | 1 | return 0; |
646 | 1 | } |
647 | | |
648 | | /* Wrap up interp instance after a "job" */ |
649 | | static int |
650 | | ps_impl_dnit_job(pl_interp_implementation_t *impl) |
651 | 3 | { |
652 | 3 | ps_interp_instance_t *psi = (ps_interp_instance_t *)impl->interp_client_data; |
653 | | |
654 | 3 | return psapi_set_device(psi->psapi_instance, NULL); |
655 | 3 | } |
656 | | |
657 | | /* Deallocate a interpreter instance */ |
658 | | static int |
659 | | ps_impl_deallocate_interp_instance(pl_interp_implementation_t *impl) |
660 | 8.09k | { |
661 | 8.09k | ps_interp_instance_t *psi = (ps_interp_instance_t *)impl->interp_client_data; |
662 | 8.09k | int code; |
663 | | |
664 | 8.09k | if (psi == NULL) |
665 | 0 | return 0; |
666 | 8.09k | code = psapi_exit(psi->psapi_instance); |
667 | 8.09k | psapi_delete_instance(psi->psapi_instance); |
668 | 8.09k | gs_free_object(psi->memory, psi, "ps_impl_allocate_interp_instance"); |
669 | | impl->interp_client_data = NULL; |
670 | 8.09k | return code; |
671 | 8.09k | } |
672 | | |
673 | | /* Parser implementation descriptor */ |
674 | | const pl_interp_implementation_t ps_implementation = { |
675 | | ps_impl_characteristics, |
676 | | ps_impl_allocate_interp_instance, |
677 | | ps_impl_get_device_memory, |
678 | | ps_impl_set_param, |
679 | | ps_impl_add_path, |
680 | | ps_impl_post_args_init, |
681 | | ps_impl_init_job, |
682 | | ps_impl_run_prefix_commands, |
683 | | ps_impl_process_file, |
684 | | ps_impl_process_begin, |
685 | | ps_impl_process, |
686 | | ps_impl_process_end, |
687 | | ps_impl_flush_to_eoj, |
688 | | ps_impl_process_eof, |
689 | | ps_impl_report_errors, |
690 | | ps_impl_dnit_job, |
691 | | ps_impl_deallocate_interp_instance, |
692 | | NULL, /* ps_impl_reset */ |
693 | | NULL /* interp_client_data */ |
694 | | }; |