/src/graphicsmagick/magick/delegate.c
Line | Count | Source |
1 | | /* |
2 | | % Copyright (C) 2003-2026 GraphicsMagick Group |
3 | | % Copyright (C) 2002 ImageMagick Studio |
4 | | % |
5 | | % This program is covered by multiple licenses, which are described in |
6 | | % Copyright.txt. You should have received a copy of Copyright.txt with this |
7 | | % package; otherwise see http://www.graphicsmagick.org/www/Copyright.html. |
8 | | % |
9 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
10 | | % % |
11 | | % % |
12 | | % DDDD EEEEE L EEEEE GGGG AAA TTTTT EEEEE % |
13 | | % D D E L E G A A T E % |
14 | | % D D EEE L EEE G GG AAAAA T EEE % |
15 | | % D D E L E G G A A T E % |
16 | | % DDDD EEEEE LLLLL EEEEE GGG A A T EEEEE % |
17 | | % % |
18 | | % % |
19 | | % Methods to Read/Write/Invoke Delegates % |
20 | | % % |
21 | | % % |
22 | | % Software Design % |
23 | | % John Cristy % |
24 | | % October 1998 % |
25 | | % % |
26 | | % % |
27 | | % % |
28 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
29 | | % |
30 | | % The Delegates methods associate a set of commands with a particular |
31 | | % image format. GraphicsMagick uses delegates for formats it does not handle |
32 | | % directly. |
33 | | % |
34 | | % Thanks to Bob Friesenhahn for the initial inspiration and design of the |
35 | | % delegates methods. |
36 | | % |
37 | | % |
38 | | */ |
39 | | |
40 | | /* |
41 | | Include declarations. |
42 | | */ |
43 | | #include "magick/studio.h" |
44 | | #include "magick/blob.h" |
45 | | #include "magick/constitute.h" |
46 | | #include "magick/delegate.h" |
47 | | #include "magick/log.h" |
48 | | #if defined(MSWINDOWS) || defined(__CYGWIN__) |
49 | | # include "magick/nt_feature.h" |
50 | | #endif |
51 | | #if defined(POSIX) |
52 | | # include "magick/unix_port.h" |
53 | | #endif |
54 | | #include "magick/semaphore.h" |
55 | | #include "magick/tempfile.h" |
56 | | #include "magick/utility.h" |
57 | | |
58 | | /* |
59 | | Define declarations. |
60 | | */ |
61 | 20 | #define DelegateFilename "delegates.mgk" |
62 | | |
63 | | /* |
64 | | Declare delegate map. |
65 | | */ |
66 | | static char |
67 | | *DelegateMap = (char *) |
68 | | "<?xml version=\"1.0\"?>" |
69 | | "<delegatemap>" |
70 | | " <delegate stealth=\"True\" />" |
71 | | "</delegatemap>"; |
72 | | |
73 | | /* |
74 | | Global declaractions. |
75 | | */ |
76 | | static SemaphoreInfo |
77 | | *delegate_semaphore = (SemaphoreInfo *) NULL; |
78 | | |
79 | | static DelegateInfo |
80 | | *delegate_list = (DelegateInfo *) NULL; |
81 | | |
82 | | /* |
83 | | Forward declaractions. |
84 | | */ |
85 | | static unsigned int |
86 | | ReadConfigureFile(const char *,const unsigned long,ExceptionInfo *); |
87 | | |
88 | | /* |
89 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
90 | | % % |
91 | | % % |
92 | | % % |
93 | | % D e s t r o y D e l e g a t e I n f o % |
94 | | % % |
95 | | % % |
96 | | % % |
97 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
98 | | % |
99 | | % Method DestroyDelegateInfo deallocates memory associated with the delegates |
100 | | % list. |
101 | | % |
102 | | % The format of the DestroyDelegateInfo method is: |
103 | | % |
104 | | % DestroyDelegateInfo(void) |
105 | | % |
106 | | */ |
107 | | MagickExport void DestroyDelegateInfo(void) |
108 | 0 | { |
109 | 0 | DelegateInfo |
110 | 0 | *delegate_info; |
111 | |
|
112 | 0 | register DelegateInfo |
113 | 0 | *p; |
114 | |
|
115 | 0 | for (p=delegate_list; p != (DelegateInfo *) NULL; ) |
116 | 0 | { |
117 | 0 | delegate_info=p; |
118 | 0 | p=p->next; |
119 | 0 | if (delegate_info->path != (char *) NULL) |
120 | 0 | MagickFreeMemory(delegate_info->path); |
121 | 0 | if (delegate_info->decode != (char *) NULL) |
122 | 0 | MagickFreeMemory(delegate_info->decode); |
123 | 0 | if (delegate_info->encode != (char *) NULL) |
124 | 0 | MagickFreeMemory(delegate_info->encode); |
125 | 0 | if (delegate_info->commands != (char *) NULL) |
126 | 0 | MagickFreeMemory(delegate_info->commands); |
127 | 0 | MagickFreeMemory(delegate_info); |
128 | 0 | } |
129 | 0 | delegate_list=(DelegateInfo *) NULL; |
130 | 0 | DestroySemaphoreInfo(&delegate_semaphore); |
131 | 0 | } |
132 | | |
133 | | /* |
134 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
135 | | % % |
136 | | % % |
137 | | % % |
138 | | % G e t D e l e g a t e C o m m a n d % |
139 | | % % |
140 | | % % |
141 | | % % |
142 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
143 | | % |
144 | | % Method GetDelegateCommand replaces any embedded formatting characters with |
145 | | % the appropriate image attribute and returns the resulting command. |
146 | | % |
147 | | % The format of the GetDelegateCommand method is: |
148 | | % |
149 | | % char *GetDelegateCommand(const ImageInfo *image_info,Image *image, |
150 | | % const char *decode,const char *encode,ExceptionInfo *exception) |
151 | | % |
152 | | % A description of each parameter follows: |
153 | | % |
154 | | % o command: Method GetDelegateCommand returns the command associated |
155 | | % with specified delegate tag. |
156 | | % |
157 | | % o image_info: The image info. |
158 | | % |
159 | | % o image: The image. |
160 | | % |
161 | | % o decode: Specifies the decode delegate we are searching for as a |
162 | | % character string. |
163 | | % |
164 | | % o encode: Specifies the encode delegate we are searching for as a |
165 | | % character string. |
166 | | % |
167 | | % o exception: Return any errors or warnings in this structure. |
168 | | % |
169 | | % |
170 | | */ |
171 | | MagickExport char *GetDelegateCommand(const ImageInfo *image_info,Image *image, |
172 | | const char *decode,const char *encode,ExceptionInfo *exception) |
173 | 0 | { |
174 | 0 | char |
175 | 0 | *command, |
176 | 0 | **commands; |
177 | |
|
178 | 0 | const DelegateInfo |
179 | 0 | *delegate_info; |
180 | |
|
181 | 0 | register long |
182 | 0 | i; |
183 | |
|
184 | 0 | assert(image_info != (ImageInfo *) NULL); |
185 | 0 | assert(image_info->signature == MagickSignature); |
186 | 0 | assert(image != (Image *) NULL); |
187 | 0 | assert(image->signature == MagickSignature); |
188 | 0 | delegate_info=GetDelegateInfo(decode,encode,exception); |
189 | 0 | if (delegate_info == (const DelegateInfo *) NULL) |
190 | 0 | { |
191 | 0 | ThrowException(exception,DelegateError,NoTagFound, |
192 | 0 | decode ? decode : encode); |
193 | 0 | return((char *) NULL); |
194 | 0 | } |
195 | 0 | commands=StringToList(delegate_info->commands); |
196 | 0 | if (commands == (char **) NULL) |
197 | 0 | { |
198 | 0 | ThrowException(exception,ResourceLimitError,MemoryAllocationFailed, |
199 | 0 | decode ? decode : encode); |
200 | 0 | return((char *) NULL); |
201 | 0 | } |
202 | 0 | command=TranslateText(image_info,image,commands[0]); |
203 | 0 | if (command == (char *) NULL) |
204 | 0 | ThrowException(exception,ResourceLimitError,MemoryAllocationFailed, |
205 | 0 | commands[0]); |
206 | | /* |
207 | | Free resources. |
208 | | */ |
209 | 0 | for (i=0; commands[i] != (char *) NULL; i++) |
210 | 0 | MagickFreeMemory(commands[i]); |
211 | 0 | MagickFreeMemory(commands); |
212 | 0 | return(command); |
213 | 0 | } |
214 | | |
215 | | /* |
216 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
217 | | % % |
218 | | % % |
219 | | % % |
220 | | % G e t D e l e g a t e I n f o % |
221 | | % % |
222 | | % % |
223 | | % % |
224 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
225 | | % |
226 | | % Method GetDelegateInfo returns any delegates associated with the specified |
227 | | % tag. |
228 | | % |
229 | | % The format of the GetDelegateInfo method is: |
230 | | % |
231 | | % const DelegateInfo *GetDelegateInfo(const char *decode, |
232 | | % const char *encode,ExceptionInfo *exception) |
233 | | % |
234 | | % A description of each parameter follows: |
235 | | % |
236 | | % o delgate_info: Method GetDelegateInfo returns any delegates associated |
237 | | % with the specified tag. |
238 | | % |
239 | | % o decode: Specifies the decode delegate we are searching for as a |
240 | | % character string. |
241 | | % |
242 | | % o encode: Specifies the encode delegate we are searching for as a |
243 | | % character string. |
244 | | % |
245 | | % o exception: Return any errors or warnings in this structure. |
246 | | % |
247 | | % |
248 | | */ |
249 | | MagickExport const DelegateInfo *GetDelegateInfo(const char *decode, |
250 | | const char *encode,ExceptionInfo *exception) |
251 | 30.0k | { |
252 | 30.0k | register DelegateInfo |
253 | 30.0k | *p; |
254 | | |
255 | 30.0k | if (delegate_list == (DelegateInfo *) NULL) |
256 | 20 | { |
257 | 20 | LockSemaphoreInfo(delegate_semaphore); |
258 | 20 | if (delegate_list == (DelegateInfo *) NULL) |
259 | 20 | (void) ReadConfigureFile(DelegateFilename,0,exception); |
260 | 20 | UnlockSemaphoreInfo(delegate_semaphore); |
261 | 20 | } |
262 | 30.0k | if ((LocaleCompare(decode,"*") == 0) && (LocaleCompare(encode,"*") == 0)) |
263 | 0 | return((const DelegateInfo *) delegate_list); |
264 | | /* |
265 | | Search for requested delegate. |
266 | | */ |
267 | 30.0k | LockSemaphoreInfo(delegate_semaphore); |
268 | 60.0k | for (p=delegate_list; p != (const DelegateInfo *) NULL; p=p->next) |
269 | 30.0k | { |
270 | 30.0k | if (p->mode > 0) |
271 | 0 | { |
272 | 0 | if (LocaleCompare(p->decode,decode) == 0) |
273 | 0 | break; |
274 | 0 | continue; |
275 | 0 | } |
276 | 30.0k | if (p->mode < 0) |
277 | 0 | { |
278 | 0 | if (LocaleCompare(p->encode,encode) == 0) |
279 | 0 | break; |
280 | 0 | continue; |
281 | 0 | } |
282 | 30.0k | if (LocaleCompare(decode,p->decode) == 0) |
283 | 0 | if (LocaleCompare(encode,p->encode) == 0) |
284 | 0 | break; |
285 | 30.0k | if (LocaleCompare(decode,"*") == 0) |
286 | 0 | if (LocaleCompare(encode,p->encode) == 0) |
287 | 0 | break; |
288 | 30.0k | if (LocaleCompare(decode,p->decode) == 0) |
289 | 0 | if (LocaleCompare(encode,"*") == 0) |
290 | 0 | break; |
291 | 30.0k | } |
292 | 30.0k | if (p != (DelegateInfo *) NULL) |
293 | 0 | if (p != delegate_list) |
294 | 0 | { |
295 | | /* |
296 | | Self-adjusting list. |
297 | | */ |
298 | 0 | if (p->previous != (DelegateInfo *) NULL) |
299 | 0 | p->previous->next=p->next; |
300 | 0 | if (p->next != (DelegateInfo *) NULL) |
301 | 0 | p->next->previous=p->previous; |
302 | 0 | p->previous=(DelegateInfo *) NULL; |
303 | 0 | p->next=delegate_list; |
304 | 0 | delegate_list->previous=p; |
305 | 0 | delegate_list=p; |
306 | 0 | } |
307 | 30.0k | UnlockSemaphoreInfo(delegate_semaphore); |
308 | 30.0k | return((const DelegateInfo *) p); |
309 | 30.0k | } |
310 | | |
311 | | /* |
312 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
313 | | % % |
314 | | % % |
315 | | % % |
316 | | % G e t P o s t s c r i p t D e l e g a t e I n f o % |
317 | | % % |
318 | | % % |
319 | | % % |
320 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
321 | | % |
322 | | % Method GetPostscriptDelegateInfo returns the Postscript delegate which |
323 | | % best supports the image type requested via ImageInfo |
324 | | % |
325 | | % The format of the GetPostscriptDelegateInfo method is: |
326 | | % |
327 | | % const DelegateInfo *(const ImageInfo *image_info, |
328 | | % ExceptionInfo *exception) |
329 | | % |
330 | | % A description of each parameter follows: |
331 | | % |
332 | | % o image_info: The 'monochrome' and 'type' fields of image_info are used |
333 | | % to select the best postscript delegate type. |
334 | | % |
335 | | % o antialias: Set to best antialias setting for this delegate based on |
336 | | % user requested antialias setting, and rendering depth. |
337 | | % |
338 | | % o exception: Return any errors or warnings in this structure. |
339 | | % |
340 | | % |
341 | | */ |
342 | | MagickExport const DelegateInfo *GetPostscriptDelegateInfo(const ImageInfo *image_info, |
343 | | unsigned int *antialias, |
344 | | ExceptionInfo *exception) |
345 | 29.7k | { |
346 | 29.7k | char |
347 | 29.7k | delegate[MaxTextExtent]; |
348 | | |
349 | 29.7k | (void) strlcpy(delegate,"gs-color",sizeof(delegate)); |
350 | 29.7k | *antialias=(image_info->antialias ? 4 : 1); |
351 | 29.7k | if ((image_info->monochrome) || (BilevelType == image_info->type)) |
352 | 0 | { |
353 | 0 | (void) strlcpy(delegate,"gs-mono",sizeof(delegate)); |
354 | 0 | *antialias=1; |
355 | 0 | } |
356 | 29.7k | else if (GrayscaleType == image_info->type) |
357 | 0 | { |
358 | 0 | (void) strlcpy(delegate,"gs-gray",sizeof(delegate)); |
359 | 0 | } |
360 | 29.7k | else if (PaletteType == image_info->type) |
361 | 0 | { |
362 | 0 | (void) strlcpy(delegate,"gs-palette",sizeof(delegate)); |
363 | 0 | } |
364 | 29.7k | else if ((GrayscaleMatteType == image_info->type) || |
365 | 29.7k | (PaletteMatteType == image_info->type) || |
366 | 29.7k | (TrueColorMatteType == image_info->type)) |
367 | 0 | { |
368 | 0 | (void) strlcpy(delegate,"gs-color+alpha",sizeof(delegate)); |
369 | 0 | } |
370 | 29.7k | else if (ColorSeparationType == image_info->type) |
371 | 0 | { |
372 | 0 | (void) strlcpy(delegate,"gs-cmyk",sizeof(delegate)); |
373 | 0 | } |
374 | 29.7k | else if (ColorSeparationMatteType == image_info->type) |
375 | 0 | { |
376 | 0 | (void) strlcpy(delegate,"gs-cmyka",sizeof(delegate)); |
377 | 0 | } |
378 | 29.7k | return GetDelegateInfo(delegate,(char *) NULL,exception); |
379 | 29.7k | } |
380 | | |
381 | | /* |
382 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
383 | | % % |
384 | | % % |
385 | | % % |
386 | | + I n i t i a l i z e D e l e g a t e I n f o % |
387 | | % % |
388 | | % % |
389 | | % % |
390 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
391 | | % |
392 | | % Method InitializeDelegateInfo initializes the delegate facility |
393 | | % |
394 | | % The format of the InitializeDelegateInfo method is: |
395 | | % |
396 | | % MagickPassFail InitializeDelegateInfo(void) |
397 | | % |
398 | | % |
399 | | */ |
400 | | MagickPassFail |
401 | | InitializeDelegateInfo(void) |
402 | 254 | { |
403 | 254 | assert(delegate_semaphore == (SemaphoreInfo *) NULL); |
404 | 254 | delegate_semaphore=AllocateSemaphoreInfo(); |
405 | 254 | return MagickPass; |
406 | 254 | } |
407 | | |
408 | | /* |
409 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
410 | | % % |
411 | | % % |
412 | | % % |
413 | | % I n v o k e D e l e g a t e % |
414 | | % % |
415 | | % % |
416 | | % % |
417 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
418 | | % |
419 | | % Method InvokeDelegate replaces any embedded formatting characters with |
420 | | % the appropriate image attribute and executes the resulting command. False |
421 | | % is returned if the commands execute with success otherwise True. |
422 | | % |
423 | | % The format of the InvokeDelegate method is: |
424 | | % |
425 | | % unsigned int InvokeDelegate(ImageInfo *image_info,Image *image, |
426 | | % const char *decode,const char *encode,ExceptionInfo *exception) |
427 | | % |
428 | | % A description of each parameter follows: |
429 | | % |
430 | | % o image_info: The imageInfo. |
431 | | % |
432 | | % o image: The image. |
433 | | % |
434 | | % o exception: Return any errors or warnings in this structure. |
435 | | % |
436 | | % |
437 | | */ |
438 | | #if defined(POSIX) |
439 | | /* |
440 | | Escape characters from the string 'src' to the string 'dst', |
441 | | limiting the number of output characters according to 'size'. |
442 | | */ |
443 | | static size_t |
444 | | UnixShellTextEscape(char *dst, const char *src, const size_t size) |
445 | 0 | { |
446 | 0 | size_t |
447 | 0 | length=0; |
448 | |
|
449 | 0 | char |
450 | 0 | *p; |
451 | |
|
452 | 0 | const char |
453 | 0 | *q; |
454 | |
|
455 | 0 | assert(dst != NULL); |
456 | 0 | assert(src != (const char *) NULL); |
457 | 0 | assert(size >= 1); |
458 | | |
459 | | /* |
460 | | Copy src to dst within bounds of size-1, while escaping special |
461 | | characters. |
462 | | */ |
463 | 0 | for ( p=dst, q=src, length=0 ; |
464 | 0 | (*q != 0) && (length < size-1) ; |
465 | 0 | length++, p++, q++ ) |
466 | 0 | { |
467 | 0 | register const char c = *q; |
468 | 0 | if ((c == '\\') || |
469 | 0 | (c == '`') || |
470 | 0 | (c == '"') || |
471 | 0 | (c == '$')) |
472 | 0 | { |
473 | 0 | if (length+1 >= size-1) |
474 | 0 | break; |
475 | 0 | *p = '\\'; |
476 | 0 | p++; |
477 | 0 | length++; |
478 | 0 | } |
479 | 0 | *p = c; |
480 | 0 | } |
481 | |
|
482 | 0 | dst[length]='\0'; |
483 | |
|
484 | 0 | return length; |
485 | 0 | } |
486 | | #endif /* POSIX */ |
487 | | |
488 | | #if defined(HAVE_SPAWNVP) /* Windows spawnvp() */ |
489 | | /* |
490 | | Escape a dynamically-allocated string argument (if needed), |
491 | | replacing with a new allocation if escaping was necessary. |
492 | | */ |
493 | | static void |
494 | | WindowsArgumentTextEscape(char **arg) |
495 | | { |
496 | | const char |
497 | | *sa; |
498 | | |
499 | | char |
500 | | *escaped; |
501 | | |
502 | | size_t |
503 | | e, |
504 | | i, |
505 | | length; |
506 | | |
507 | | MagickBool |
508 | | do_escape=MagickFalse; |
509 | | |
510 | | /* |
511 | | Compute length, allowing for escape characters. |
512 | | |
513 | | It is possible that other characters should be escaped, but we are |
514 | | unaware of the specific characters or the correct syntax. The |
515 | | characters and syntax might depend on the version of Windows or |
516 | | the Windows CRT used. |
517 | | */ |
518 | | e=0; |
519 | | length=0; |
520 | | sa=*arg; |
521 | | for ( i=0; sa[i] != '\0'; i++) |
522 | | { |
523 | | if (isspace((int) sa[i])) |
524 | | { |
525 | | length += 2; |
526 | | do_escape=MagickTrue; |
527 | | } |
528 | | length++; |
529 | | } |
530 | | length++; /* null */ |
531 | | if (do_escape) |
532 | | { |
533 | | /* |
534 | | Allocate buffer |
535 | | */ |
536 | | escaped=MagickAllocateMemory(char *,length); |
537 | | /* |
538 | | Escape into buffer |
539 | | */ |
540 | | if (escaped != (char *) NULL) |
541 | | { |
542 | | sa=*arg; |
543 | | e=0; |
544 | | for ( i=0; sa[i] != '\0'; i++) |
545 | | { |
546 | | char c=sa[i]; |
547 | | if (isspace((int) c)) |
548 | | { |
549 | | escaped[e++]='"'; |
550 | | escaped[e++]=c; |
551 | | escaped[e++]='"'; |
552 | | } |
553 | | else |
554 | | { |
555 | | escaped[e++]=c; |
556 | | } |
557 | | } |
558 | | escaped[e]='\0'; |
559 | | MagickFreeMemory(*arg); |
560 | | *arg = escaped; |
561 | | } |
562 | | } |
563 | | } |
564 | | #endif /* defined(HAVE_SPAWNVP) */ |
565 | | #if defined(MSWINDOWS) |
566 | | /* |
567 | | Escape characters from the string 'src' to the string 'dst', |
568 | | limiting the number of output characters according to 'size'. |
569 | | */ |
570 | | static size_t |
571 | | WindowsShellTextEscape(char *dst, const char *src, const size_t size) |
572 | | { |
573 | | size_t |
574 | | length=0; |
575 | | |
576 | | char |
577 | | *p; |
578 | | |
579 | | const char |
580 | | *q; |
581 | | |
582 | | assert(dst != NULL); |
583 | | assert(src != (const char *) NULL); |
584 | | assert(size >= 1); |
585 | | |
586 | | |
587 | | /* |
588 | | Copy src to dst within bounds of size-1, while escaping special |
589 | | characters. |
590 | | */ |
591 | | for ( p=dst, q=src, length=0 ; |
592 | | (*q != 0) && (length < size-1) ; |
593 | | length++, p++, q++ ) |
594 | | { |
595 | | register const char c = *q; |
596 | | #if 0 |
597 | | /* |
598 | | FIXME: Currently the correct implementation is not known so we |
599 | | don't alter arguments at the moment. |
600 | | */ |
601 | | if ((c == '\\') || |
602 | | (c == '"') || |
603 | | (c == '%%')) |
604 | | { |
605 | | if (length+1 >= size-1) |
606 | | break; |
607 | | *p = '\\'; |
608 | | p++; |
609 | | length++; |
610 | | } |
611 | | #endif |
612 | | *p = c; |
613 | | } |
614 | | |
615 | | dst[length]='\0'; |
616 | | |
617 | | return length; |
618 | | } |
619 | | #endif /* MSWINDOWS */ |
620 | | |
621 | | MagickExport unsigned int InvokeDelegate(ImageInfo *image_info,Image *image, |
622 | | const char *decode,const char *encode,ExceptionInfo *exception) |
623 | 0 | { |
624 | 0 | char |
625 | 0 | *command, |
626 | 0 | **commands, |
627 | 0 | filename[MaxTextExtent]; |
628 | |
|
629 | 0 | const DelegateInfo |
630 | 0 | *delegate_info; |
631 | |
|
632 | 0 | register long |
633 | 0 | i; |
634 | |
|
635 | 0 | unsigned int |
636 | 0 | status, |
637 | 0 | temporary_image_filename; |
638 | | |
639 | | /* |
640 | | Get delegate. |
641 | | */ |
642 | 0 | assert(image_info != (ImageInfo *) NULL); |
643 | 0 | assert(image_info->signature == MagickSignature); |
644 | 0 | assert(image != (Image *) NULL); |
645 | 0 | assert(image->signature == MagickSignature); |
646 | 0 | temporary_image_filename=(*image->filename == '\0'); |
647 | 0 | if (temporary_image_filename) |
648 | 0 | { |
649 | | /* Allocate a temporary filename if image is unnamed. */ |
650 | 0 | if(!AcquireTemporaryFileName(image->filename)) |
651 | 0 | { |
652 | 0 | (void) ThrowException(exception,FileOpenError,UnableToCreateTemporaryFile,image->filename); |
653 | 0 | return(False); |
654 | 0 | } |
655 | 0 | } |
656 | 0 | (void) strlcpy(filename,image->filename,MaxTextExtent); |
657 | 0 | delegate_info=GetDelegateInfo(decode,encode,exception); |
658 | 0 | if (delegate_info == (DelegateInfo *) NULL) |
659 | 0 | { |
660 | 0 | if (temporary_image_filename) |
661 | 0 | (void) LiberateTemporaryFile(image->filename); |
662 | 0 | (void) ThrowException(exception,DelegateError,NoTagFound, |
663 | 0 | decode ? decode : encode); |
664 | 0 | return(False); |
665 | 0 | } |
666 | | |
667 | 0 | if (*image_info->filename == '\0') |
668 | 0 | { |
669 | | /* ReadImage will normally have already set image_info->filename |
670 | | to the name of a temporary file. If not, then assign |
671 | | one. Setting image_info->temporary to True indicates that |
672 | | there is a temporary file to be removed later. */ |
673 | 0 | if(!AcquireTemporaryFileName(image_info->filename)) |
674 | 0 | { |
675 | 0 | if (temporary_image_filename) |
676 | 0 | (void) LiberateTemporaryFile(image->filename); |
677 | 0 | (void) ThrowException(exception,FileOpenError,UnableToCreateTemporaryFile,image_info->filename); |
678 | 0 | return(False); |
679 | 0 | } |
680 | 0 | image_info->temporary=True; |
681 | 0 | } |
682 | | |
683 | 0 | if (delegate_info->mode != 0) |
684 | 0 | if ((decode && (delegate_info->encode != (char *) NULL)) || |
685 | 0 | (encode && (delegate_info->decode != (char *) NULL))) |
686 | 0 | { |
687 | 0 | char |
688 | 0 | decode_filename[MaxTextExtent], |
689 | 0 | *magick; |
690 | |
|
691 | 0 | ImageInfo |
692 | 0 | *clone_info; |
693 | |
|
694 | 0 | register Image |
695 | 0 | *p; |
696 | | |
697 | | /* |
698 | | Delegate requires a particular image format. |
699 | | */ |
700 | |
|
701 | 0 | if (!AcquireTemporaryFileName(image_info->unique)) |
702 | 0 | { |
703 | 0 | if (temporary_image_filename) |
704 | 0 | (void) LiberateTemporaryFile(image->filename); |
705 | 0 | (void) ThrowException(exception,FileOpenError,UnableToCreateTemporaryFile,image_info->unique); |
706 | 0 | return(False); |
707 | 0 | } |
708 | | |
709 | 0 | if (!AcquireTemporaryFileName(image_info->zero)) |
710 | 0 | { |
711 | 0 | if (temporary_image_filename) |
712 | 0 | (void) LiberateTemporaryFile(image->filename); |
713 | 0 | (void) LiberateTemporaryFile(image_info->unique); |
714 | 0 | (void) ThrowException(exception,FileOpenError,UnableToCreateTemporaryFile,image_info->zero); |
715 | 0 | return(False); |
716 | 0 | } |
717 | | /* Expand sprintf-style codes in delegate command to command string */ |
718 | 0 | magick=TranslateText(image_info,image,decode != (char *) NULL ? |
719 | 0 | delegate_info->encode : delegate_info->decode); |
720 | 0 | if (magick == (char *) NULL) |
721 | 0 | { |
722 | 0 | (void) LiberateTemporaryFile(image_info->unique); |
723 | 0 | (void) LiberateTemporaryFile(image_info->zero); |
724 | 0 | if (temporary_image_filename) |
725 | 0 | (void) LiberateTemporaryFile(image->filename); |
726 | 0 | (void) ThrowException(exception,DelegateError,DelegateFailed, |
727 | 0 | decode ? decode : encode); |
728 | 0 | return(False); |
729 | 0 | } |
730 | 0 | LocaleUpper(magick); |
731 | 0 | clone_info=CloneImageInfo(image_info); |
732 | 0 | (void) strlcpy((char *) clone_info->magick,magick,MaxTextExtent); |
733 | 0 | (void) strlcpy(image->magick,magick,MaxTextExtent); |
734 | 0 | MagickFreeMemory(magick); |
735 | 0 | (void) strlcpy(decode_filename,image->filename,MaxTextExtent); |
736 | 0 | MagickFormatString(clone_info->filename,sizeof(clone_info->filename),"%.1024s:",delegate_info->decode); |
737 | 0 | (void) SetImageInfo(clone_info,SETMAGICK_WRITE,exception); |
738 | 0 | (void) strlcpy(clone_info->filename,image_info->filename, |
739 | 0 | MaxTextExtent); |
740 | 0 | for (p=image; p != (Image *) NULL; p=p->next) |
741 | 0 | { |
742 | 0 | MagickFormatString(p->filename,sizeof(p->filename),"%.1024s:%.1024s",delegate_info->decode, |
743 | 0 | decode_filename); |
744 | 0 | status=WriteImage(clone_info,p); |
745 | 0 | if (status == False) |
746 | 0 | { |
747 | 0 | (void) LiberateTemporaryFile(image_info->unique); |
748 | 0 | (void) LiberateTemporaryFile(image_info->zero); |
749 | 0 | if (temporary_image_filename) |
750 | 0 | (void) LiberateTemporaryFile(image->filename); |
751 | 0 | DestroyImageInfo(clone_info); |
752 | 0 | (void) ThrowException(exception,DelegateError,DelegateFailed, |
753 | 0 | decode ? decode : encode); |
754 | 0 | return(False); |
755 | 0 | } |
756 | 0 | if (clone_info->adjoin) |
757 | 0 | break; |
758 | 0 | } |
759 | 0 | (void) LiberateTemporaryFile(image_info->unique); |
760 | 0 | (void) LiberateTemporaryFile(image_info->zero); |
761 | 0 | DestroyImageInfo(clone_info); |
762 | 0 | } |
763 | | /* |
764 | | Invoke delegate. |
765 | | */ |
766 | 0 | (void) strlcpy(image->filename,filename,MaxTextExtent); |
767 | 0 | commands=StringToList(delegate_info->commands); |
768 | 0 | if (commands == (char **) NULL) |
769 | 0 | { |
770 | 0 | if (temporary_image_filename) |
771 | 0 | (void) LiberateTemporaryFile(image->filename); |
772 | 0 | (void) ThrowException(exception,ResourceLimitError,MemoryAllocationFailed,decode ? decode : encode); |
773 | 0 | return(False); |
774 | 0 | } |
775 | 0 | command=(char *) NULL; |
776 | 0 | status=True; |
777 | | /* For each delegate command ... */ |
778 | 0 | for (i=0; commands[i] != (char *) NULL; i++) |
779 | 0 | { |
780 | 0 | status=True; |
781 | | /* Allocate convenience temporary files */ |
782 | 0 | if (!AcquireTemporaryFileName(image_info->unique)) |
783 | 0 | { |
784 | 0 | (void) ThrowException(exception,FileOpenError,UnableToCreateTemporaryFile,image_info->unique); |
785 | 0 | status=False; |
786 | 0 | goto error_exit; |
787 | 0 | } |
788 | 0 | if (!AcquireTemporaryFileName(image_info->zero)) |
789 | 0 | { |
790 | 0 | (void) ThrowException(exception,FileOpenError,UnableToCreateTemporaryFile,image_info->zero); |
791 | 0 | (void) LiberateTemporaryFile(image_info->unique); |
792 | 0 | status=False; |
793 | 0 | goto error_exit; |
794 | 0 | } |
795 | 0 | { |
796 | 0 | MagickBool |
797 | 0 | needs_shell; |
798 | | |
799 | | /* |
800 | | Check to see if command template must be executed via shell |
801 | | due to using constructs requiring multiple processes or I/O |
802 | | redirection. |
803 | | */ |
804 | 0 | needs_shell = MagickFalse; |
805 | 0 | { |
806 | 0 | char * |
807 | 0 | p; |
808 | |
|
809 | 0 | p = commands[i]; |
810 | 0 | for (p = commands[i]; *p; p++) |
811 | 0 | { |
812 | 0 | if (('&' == *p) || |
813 | 0 | (';' == *p) || |
814 | 0 | ('<' == *p) || |
815 | 0 | ('>' == *p) || |
816 | 0 | ('|' == *p)) |
817 | 0 | { |
818 | 0 | needs_shell = MagickTrue; |
819 | 0 | break; |
820 | 0 | } |
821 | 0 | } |
822 | 0 | } |
823 | |
|
824 | 0 | if (MagickFalse == needs_shell) |
825 | 0 | { |
826 | 0 | int |
827 | 0 | arg_count, |
828 | 0 | j; |
829 | |
|
830 | 0 | char |
831 | 0 | **arg_array; |
832 | | |
833 | | /* |
834 | | Convert command template into an argument array. Translate |
835 | | each argument array element individually in order to |
836 | | absolutely avoid any possibility that the number of arguments |
837 | | may be altered due to substituted data. |
838 | | */ |
839 | 0 | arg_array = StringToArgv(commands[i],&arg_count); |
840 | 0 | for (j = 0; arg_array[j] != (const char*) NULL; j++) |
841 | 0 | { |
842 | 0 | if (strchr(arg_array[j], '%') != (const char*) NULL) |
843 | 0 | { |
844 | 0 | char *expanded = TranslateText(image_info,image,arg_array[j]); |
845 | 0 | if (expanded != (char *) NULL) |
846 | 0 | { |
847 | 0 | MagickFreeMemory(arg_array[j]); |
848 | 0 | arg_array[j] = expanded; |
849 | 0 | } |
850 | 0 | } |
851 | | #if defined(HAVE_SPAWNVP) |
852 | | /* |
853 | | Windows _spawnvp() pretends to offer an argv style |
854 | | interface but actually splits arguments into |
855 | | additional arguments based on white-space. It might |
856 | | have more wonderful properties we are not aware of |
857 | | yet. Escape white-space in arguments with double |
858 | | quotes to avoid the splitting. |
859 | | |
860 | | This code should likely be in MagickSpawnVP() but then |
861 | | we would need to clone the input array so it can be |
862 | | modified. |
863 | | |
864 | | This undocumented feature is an example of why |
865 | | Microsoft Windows can not be a secure operating |
866 | | system. |
867 | | */ |
868 | | WindowsArgumentTextEscape(&arg_array[j]); |
869 | | #endif |
870 | 0 | } |
871 | | /* |
872 | | Execute delegate using our secure "spawn" facility. |
873 | | */ |
874 | 0 | status = MagickSpawnVP(image_info->verbose,arg_array[1],arg_array+1); |
875 | 0 | for (j = 0; arg_array[j] != (const char*) NULL; j++) |
876 | 0 | MagickFreeMemory(arg_array[j]); |
877 | 0 | MagickFreeMemory(arg_array); |
878 | 0 | } |
879 | 0 | else |
880 | 0 | { |
881 | | /* |
882 | | Expand sprintf-style codes in delegate command to command |
883 | | string, escaping replacement text appropriately |
884 | | */ |
885 | 0 | command=TranslateTextEx(image_info,image,commands[i], |
886 | 0 | #if defined(POSIX) |
887 | 0 | UnixShellTextEscape |
888 | 0 | #endif /* POSIX */ |
889 | | #if defined(MSWINDOWS) |
890 | | WindowsShellTextEscape |
891 | | #endif /* MSWINDOWS */ |
892 | 0 | ); |
893 | 0 | if (command == (char *) NULL) |
894 | 0 | break; |
895 | | /* |
896 | | Execute delegate using command shell. |
897 | | */ |
898 | 0 | status=SystemCommand(image_info->verbose,command); |
899 | 0 | } |
900 | 0 | } |
901 | 0 | MagickFreeMemory(command); |
902 | | /* Liberate convenience temporary files */ |
903 | 0 | (void) LiberateTemporaryFile(image_info->unique); |
904 | 0 | (void) LiberateTemporaryFile(image_info->zero); |
905 | 0 | if (status != False) |
906 | 0 | { |
907 | 0 | (void) ThrowException(exception,DelegateError,DelegateFailed, |
908 | 0 | commands[i]); |
909 | 0 | goto error_exit; |
910 | 0 | } |
911 | 0 | MagickFreeMemory(commands[i]); |
912 | 0 | } |
913 | | /* |
914 | | Free resources. |
915 | | */ |
916 | 0 | error_exit: |
917 | 0 | if (temporary_image_filename) |
918 | 0 | (void) LiberateTemporaryFile(image->filename); |
919 | 0 | for ( ; commands[i] != (char *) NULL; i++) |
920 | 0 | MagickFreeMemory(commands[i]); |
921 | 0 | MagickFreeMemory(commands); |
922 | 0 | return(status != False); |
923 | 0 | } |
924 | | |
925 | | /* |
926 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
927 | | % % |
928 | | % % |
929 | | % % |
930 | | + I n v o k e P o s t s c r i p t D e l e g a t e % |
931 | | % % |
932 | | % % |
933 | | % % |
934 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
935 | | % |
936 | | % InvokePostscriptDelegate() executes the postscript interpreter with the |
937 | | % specified command. |
938 | | % |
939 | | % The format of the InvokePostscriptDelegate method is: |
940 | | % |
941 | | % MagickPassFail InvokePostscriptDelegate(const unsigned int verbose, |
942 | | % const char *command, ExceptionInfo *exception) |
943 | | % |
944 | | % A description of each parameter follows: |
945 | | % |
946 | | % o status: Method InvokePostscriptDelegate returns MagickPass if the command |
947 | | % is successfully executed, otherwise MagickFail. |
948 | | % |
949 | | % o verbose: A value other than zero displays the command prior to |
950 | | % executing it. |
951 | | % |
952 | | % o command: The address of a character string containing the command to |
953 | | % execute. The command is formulated through direct MagickFormatString() |
954 | | % substitutions rather than using TranslateText. |
955 | | % |
956 | | % |
957 | | */ |
958 | | MagickExport MagickPassFail |
959 | | InvokePostscriptDelegate(const unsigned int verbose, |
960 | | const char *command,ExceptionInfo *exception) |
961 | 0 | { |
962 | 0 | int |
963 | 0 | status; |
964 | |
|
965 | 0 | #if defined(HasGS) |
966 | |
|
967 | 0 | register long |
968 | 0 | i; |
969 | |
|
970 | 0 | char |
971 | 0 | **argv; |
972 | |
|
973 | 0 | int |
974 | 0 | argc; |
975 | |
|
976 | | #if (defined(HasGSLIB) || defined(MSWINDOWS)) |
977 | | |
978 | | gs_main_instance |
979 | | *interpreter; |
980 | | |
981 | | int |
982 | | pexit_code; |
983 | | |
984 | | #if defined(MSWINDOWS) |
985 | | const GhostscriptVectors |
986 | | *gs_func; |
987 | | |
988 | | gs_func=NTGhostscriptDLLVectors(); |
989 | | #elif defined(HasGSLIB) |
990 | | GhostscriptVectors |
991 | | gs_func_struct; |
992 | | |
993 | | const GhostscriptVectors |
994 | | *gs_func; |
995 | | |
996 | | gs_func=(&gs_func_struct); |
997 | | gs_func_struct.exit=gsapi_exit; |
998 | | gs_func_struct.init_with_args=gsapi_init_with_args; |
999 | | gs_func_struct.new_instance=gsapi_new_instance; |
1000 | | gs_func_struct.run_string=gsapi_run_string; |
1001 | | gs_func_struct.delete_instance=gsapi_delete_instance; |
1002 | | #endif |
1003 | | if (gs_func != (GhostscriptVectors *) NULL) |
1004 | | { |
1005 | | |
1006 | | /* |
1007 | | Allocate an interpreter. |
1008 | | */ |
1009 | | interpreter = (gs_main_instance *) NULL; |
1010 | | status=(gs_func->new_instance)(&interpreter,(void *) NULL); |
1011 | | if (status < 0) |
1012 | | { |
1013 | | ThrowException(exception,DelegateError, |
1014 | | FailedToAllocateGhostscriptInterpreter,command); |
1015 | | return(MagickFail); |
1016 | | } |
1017 | | /* |
1018 | | Initialize interpreter with argument list. |
1019 | | */ |
1020 | | argv=StringToArgv(command,&argc); |
1021 | | if (argv == (char **) NULL) |
1022 | | { |
1023 | | ThrowException(exception,DelegateError,FailedToAllocateArgumentList, |
1024 | | command); |
1025 | | return(MagickFail); |
1026 | | } |
1027 | | |
1028 | | if (verbose) |
1029 | | { |
1030 | | char |
1031 | | buffer[MaxTextExtent]; |
1032 | | |
1033 | | #if defined(MSWINDOWS) |
1034 | | (void) NTGhostscriptDLL(buffer,sizeof(buffer)); |
1035 | | #else |
1036 | | (void) strlcpy(buffer,"[ghostscript library]",sizeof(buffer)); |
1037 | | #endif |
1038 | | (void) fputs(buffer,stderr); |
1039 | | for (i=2 ; i < argc ; i++) |
1040 | | (void) fprintf(stderr," \"%s\"",argv[i]); |
1041 | | (void) fflush(stderr); |
1042 | | } |
1043 | | status=(gs_func->init_with_args)(interpreter,argc-1,argv+1); |
1044 | | if (status == 0) |
1045 | | { |
1046 | | status=(gs_func->run_string) |
1047 | | (interpreter,"systemdict /start get exec\n",0,&pexit_code); |
1048 | | if ((status == 0) || (status <= -100)) |
1049 | | { |
1050 | | char |
1051 | | reason[MaxTextExtent]; |
1052 | | |
1053 | | MagickFormatString(reason,sizeof(reason),"Ghostscript returns status %d, exit code %d", |
1054 | | status,pexit_code); |
1055 | | (void) LogMagickEvent(CoderEvent,GetMagickModule(),"%s",reason); |
1056 | | ThrowException(exception,DelegateError,PostscriptDelegateFailed,command); |
1057 | | } |
1058 | | } |
1059 | | /* |
1060 | | Exit interpreter. |
1061 | | */ |
1062 | | (gs_func->exit)(interpreter); |
1063 | | /* |
1064 | | Deallocate interpreter. |
1065 | | */ |
1066 | | (gs_func->delete_instance)(interpreter); |
1067 | | for (i=0; i < argc; i++) |
1068 | | MagickFreeMemory(argv[i]); |
1069 | | MagickFreeMemory(argv); |
1070 | | if ((status == 0) || (status <= -100)) |
1071 | | { |
1072 | | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1073 | | "Returning with failure"); |
1074 | | return(MagickFail); |
1075 | | } |
1076 | | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1077 | | "Returning with success"); |
1078 | | return(MagickPass); |
1079 | | } |
1080 | | #endif /* defined(HasGSLIB) || defined(MSWINDOWS) */ |
1081 | |
|
1082 | 0 | status=MagickFail; |
1083 | 0 | { |
1084 | | /* |
1085 | | Build Ghostscript command argument list |
1086 | | */ |
1087 | 0 | argv = StringToArgv(command,&argc); |
1088 | 0 | if (argv == (char **) NULL) |
1089 | 0 | { |
1090 | 0 | ThrowException(exception,DelegateError, |
1091 | 0 | FailedToAllocateArgumentList, |
1092 | 0 | command); |
1093 | 0 | } |
1094 | 0 | else |
1095 | 0 | { |
1096 | 0 | if (strlen(argv[1]) == 0) |
1097 | 0 | { |
1098 | | /* |
1099 | | argv[1] can be empty under Windows due to empty |
1100 | | command substitution text. |
1101 | | */ |
1102 | 0 | ThrowException(exception,DelegateError, |
1103 | 0 | FailedToFindGhostscript, |
1104 | 0 | command); |
1105 | 0 | status=MagickFail; |
1106 | 0 | } |
1107 | 0 | else |
1108 | 0 | { |
1109 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1110 | 0 | "Invoking Ghostscript utility command"); |
1111 | 0 | if (MagickSpawnVP(verbose,argv[1],argv+1) == 0) |
1112 | 0 | status=MagickPass; |
1113 | 0 | } |
1114 | 0 | for (i=0; i < argc; i++) |
1115 | 0 | MagickFreeMemory(argv[i]); |
1116 | 0 | MagickFreeMemory(argv); |
1117 | 0 | } |
1118 | 0 | } |
1119 | | #else |
1120 | | |
1121 | | /* |
1122 | | Ghostscript is not supported at all! |
1123 | | */ |
1124 | | (void) verbose; |
1125 | | (void) command; |
1126 | | (void) exception; |
1127 | | |
1128 | | status = MagickFail; |
1129 | | |
1130 | | #endif /* if defined(HasGS) */ |
1131 | |
|
1132 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1133 | 0 | "Returning with %s", status == MagickFail ? |
1134 | 0 | "failure" : "success"); |
1135 | |
|
1136 | 0 | return status; |
1137 | 0 | } |
1138 | | |
1139 | | /* |
1140 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1141 | | % % |
1142 | | % % |
1143 | | % % |
1144 | | % L i s t D e l e g a t e I n f o % |
1145 | | % % |
1146 | | % % |
1147 | | % % |
1148 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1149 | | % |
1150 | | % Method ListDelegateInfo lists the image formats to a file. |
1151 | | % |
1152 | | % The format of the ListDelegateInfo method is: |
1153 | | % |
1154 | | % unsigned int ListDelegateInfo(FILE *file,ExceptionInfo *exception) |
1155 | | % |
1156 | | % A description of each parameter follows. |
1157 | | % |
1158 | | % o file: An pointer to a FILE. |
1159 | | % |
1160 | | % o exception: Return any errors or warnings in this structure. |
1161 | | % |
1162 | | % |
1163 | | */ |
1164 | | MagickExport unsigned int ListDelegateInfo(FILE *file,ExceptionInfo *exception) |
1165 | 0 | { |
1166 | 0 | char |
1167 | 0 | **commands, |
1168 | 0 | delegate[MaxTextExtent]; |
1169 | |
|
1170 | 0 | register long |
1171 | 0 | i; |
1172 | |
|
1173 | 0 | register const DelegateInfo |
1174 | 0 | *p; |
1175 | |
|
1176 | 0 | if (file == (const FILE *) NULL) |
1177 | 0 | file=stdout; |
1178 | 0 | (void) GetDelegateInfo("*","*",exception); |
1179 | 0 | LockSemaphoreInfo(delegate_semaphore); |
1180 | 0 | for (p=delegate_list; p != (const DelegateInfo *) NULL; p=p->next) |
1181 | 0 | { |
1182 | 0 | if ((p->previous == (DelegateInfo *) NULL) || |
1183 | 0 | (LocaleCompare(p->path,p->previous->path) != 0)) |
1184 | 0 | { |
1185 | 0 | if (p->previous != (DelegateInfo *) NULL) |
1186 | 0 | (void) fprintf(file,"\n"); |
1187 | 0 | if (p->path != (char *) NULL) |
1188 | 0 | (void) fprintf(file,"Path: %.1024s\n\n",p->path); |
1189 | 0 | (void) fprintf(file,"Delegate Command\n"); |
1190 | 0 | (void) fprintf(file,"-------------------------------------------------" |
1191 | 0 | "------------------------------\n"); |
1192 | 0 | } |
1193 | 0 | if (p->stealth) |
1194 | 0 | continue; |
1195 | 0 | *delegate='\0'; |
1196 | 0 | if (p->encode != (char *) NULL) |
1197 | 0 | (void) strlcpy(delegate,p->encode,sizeof(delegate)); |
1198 | 0 | (void) strlcat(delegate," ",sizeof(delegate)); |
1199 | 0 | delegate[8]='\0'; |
1200 | 0 | commands=StringToList(p->commands); |
1201 | 0 | if (commands == (char **) NULL) |
1202 | 0 | continue; |
1203 | 0 | { |
1204 | 0 | size_t |
1205 | 0 | command_length, |
1206 | 0 | length=0; |
1207 | |
|
1208 | 0 | int |
1209 | 0 | command_start_column, |
1210 | 0 | formatted_chars=0, |
1211 | 0 | screen_width=79, |
1212 | 0 | strip_length; |
1213 | |
|
1214 | 0 | char |
1215 | 0 | *s; |
1216 | | |
1217 | | /* Format output so that command spans multiple lines if |
1218 | | necessary */ |
1219 | 0 | { |
1220 | 0 | const char * const columns_env = getenv("COLUMNS"); |
1221 | 0 | if (columns_env) |
1222 | 0 | screen_width=MagickAtoI(columns_env)-1; |
1223 | 0 | } |
1224 | 0 | command_length=strlen(commands[0]); |
1225 | 0 | command_start_column=fprintf(file,"%8s%c=%c%s ",p->decode ? p->decode : "", |
1226 | 0 | p->mode <= 0 ? '<' : ' ',p->mode >= 0 ? '>' : ' ',delegate); |
1227 | 0 | for (s=commands[0]; length < command_length; s+=formatted_chars) |
1228 | 0 | { |
1229 | 0 | if (s != commands[0]) |
1230 | 0 | (void) fprintf(file,"%*s",command_start_column,""); |
1231 | 0 | strip_length=screen_width-command_start_column; |
1232 | 0 | if (length+strip_length < command_length) |
1233 | 0 | { |
1234 | 0 | char |
1235 | 0 | *e; |
1236 | |
|
1237 | 0 | for(e=s+strip_length; (*e != ' ') && (e > s) ; e--); |
1238 | 0 | strip_length=e-s; |
1239 | 0 | } |
1240 | 0 | formatted_chars=fprintf(file,"%.*s",strip_length,s); |
1241 | 0 | length+=formatted_chars; |
1242 | 0 | (void) fprintf(file,"\n"); |
1243 | 0 | if (formatted_chars <= 0) |
1244 | 0 | break; |
1245 | 0 | } |
1246 | 0 | } |
1247 | 0 | for (i=0; commands[i] != (char *) NULL; i++) |
1248 | 0 | MagickFreeMemory(commands[i]); |
1249 | 0 | MagickFreeMemory(commands); |
1250 | 0 | } |
1251 | 0 | (void) fflush(file); |
1252 | 0 | UnlockSemaphoreInfo(delegate_semaphore); |
1253 | 0 | return(True); |
1254 | 0 | } |
1255 | | |
1256 | | /* |
1257 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1258 | | % % |
1259 | | % % |
1260 | | % % |
1261 | | + R e a d C o n f i g u r e F i l e % |
1262 | | % % |
1263 | | % % |
1264 | | % % |
1265 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1266 | | % |
1267 | | % Method ReadConfigureFile reads the delegate configuration file which maps |
1268 | | % delegate invocation strings to a particular image format. |
1269 | | % |
1270 | | % The format of the ReadConfigureFile method is: |
1271 | | % |
1272 | | % unsigned int ReadConfigureFile(const char *basename, |
1273 | | % const unsigned long depth,ExceptionInfo *exception) |
1274 | | % |
1275 | | % A description of each parameter follows: |
1276 | | % |
1277 | | % o status: Method ReadConfigureFile returns True if a matching |
1278 | | % entry is found, otherwise False. |
1279 | | % |
1280 | | % o basename: The color configuration filename. |
1281 | | % |
1282 | | % o depth: depth of <include /> statements. |
1283 | | % |
1284 | | % o exception: Return any errors or warnings in this structure. |
1285 | | % |
1286 | | % |
1287 | | */ |
1288 | | #if defined(MSWINDOWS) |
1289 | | static void CatDelegatePath(char *path, |
1290 | | const char *binpath, |
1291 | | const char *command) |
1292 | | { |
1293 | | strlcpy(path,binpath,MaxTextExtent); |
1294 | | strlcat(path,command,MaxTextExtent); |
1295 | | if (IsAccessibleNoLogging(path)) |
1296 | | return; |
1297 | | |
1298 | | strlcpy(path,command,MaxTextExtent); |
1299 | | return; |
1300 | | } |
1301 | | #endif /* defined(MSWINDOWS) */ |
1302 | | static unsigned int ReadConfigureFile(const char *basename, |
1303 | | const unsigned long depth,ExceptionInfo *exception) |
1304 | 20 | { |
1305 | 20 | char |
1306 | 20 | keyword[MaxTextExtent], |
1307 | 20 | path[MaxTextExtent], |
1308 | 20 | *q, |
1309 | 20 | *token, |
1310 | 20 | *xml; |
1311 | | |
1312 | 20 | size_t |
1313 | 20 | length, |
1314 | 20 | token_max_length; |
1315 | | |
1316 | | /* |
1317 | | Read the delegates configure file. |
1318 | | */ |
1319 | 20 | (void) strlcpy(path,basename,sizeof(path)); |
1320 | 20 | if (depth == 0) |
1321 | 20 | xml=(char *) GetConfigureBlob(basename,path,&length,exception); |
1322 | 0 | else |
1323 | 0 | xml=(char *) FileToBlob(basename,&length,exception); |
1324 | 20 | if (xml == (char *) NULL) |
1325 | 20 | xml=AllocateString(DelegateMap); |
1326 | 20 | token=AllocateString(xml); |
1327 | 20 | token_max_length=strlen(token); |
1328 | 220 | for (q=xml; *q != '\0'; ) |
1329 | 200 | { |
1330 | | /* |
1331 | | Interpret XML. |
1332 | | */ |
1333 | 200 | MagickGetToken(q,&q,token,token_max_length); |
1334 | 200 | if (*token == '\0') |
1335 | 0 | break; |
1336 | 200 | (void) strlcpy(keyword,token,MaxTextExtent); |
1337 | 200 | if (LocaleNCompare(keyword,"<!--",4) == 0) |
1338 | 0 | { |
1339 | | /* |
1340 | | Comment element. |
1341 | | */ |
1342 | 0 | while ((LocaleNCompare(q,"->",2) != 0) && (*q != '\0')) |
1343 | 0 | MagickGetToken(q,&q,token,token_max_length); |
1344 | 0 | continue; |
1345 | 0 | } |
1346 | 200 | if (LocaleCompare(keyword,"<include") == 0) |
1347 | 0 | { |
1348 | | /* |
1349 | | Include element. |
1350 | | */ |
1351 | 0 | while ((*token != '>') && (*q != '\0')) |
1352 | 0 | { |
1353 | 0 | (void) strlcpy(keyword,token,MaxTextExtent); |
1354 | 0 | MagickGetToken(q,&q,token,token_max_length); |
1355 | 0 | if (*token != '=') |
1356 | 0 | continue; |
1357 | 0 | MagickGetToken(q,&q,token,token_max_length); |
1358 | 0 | if (LocaleCompare(keyword,"file") == 0) |
1359 | 0 | { |
1360 | 0 | if (depth > 200) |
1361 | 0 | ThrowException(exception,ConfigureError,IncludeElementNestedTooDeeply,path); |
1362 | 0 | else |
1363 | 0 | { |
1364 | 0 | char |
1365 | 0 | filename[MaxTextExtent]; |
1366 | |
|
1367 | 0 | GetPathComponent(path,HeadPath,filename); |
1368 | 0 | if (*filename != '\0') |
1369 | 0 | (void) strlcat(filename,DirectorySeparator,MaxTextExtent); |
1370 | 0 | (void) strlcat(filename,token,MaxTextExtent); |
1371 | 0 | (void) ReadConfigureFile(filename,depth+1,exception); |
1372 | 0 | } |
1373 | 0 | if (delegate_list != (DelegateInfo *) NULL) |
1374 | 0 | while (delegate_list->next != (DelegateInfo *) NULL) |
1375 | 0 | delegate_list=delegate_list->next; |
1376 | 0 | } |
1377 | 0 | } |
1378 | 0 | continue; |
1379 | 0 | } |
1380 | 200 | if (LocaleCompare(keyword,"<delegate") == 0) |
1381 | 20 | { |
1382 | 20 | DelegateInfo |
1383 | 20 | *delegate_info; |
1384 | | |
1385 | | /* |
1386 | | Allocate memory for the delegate list. |
1387 | | */ |
1388 | 20 | delegate_info=MagickAllocateMemory(DelegateInfo *,sizeof(DelegateInfo)); |
1389 | 20 | if (delegate_info == (DelegateInfo *) NULL) |
1390 | 0 | MagickFatalError3(ResourceLimitFatalError,MemoryAllocationFailed, |
1391 | 20 | UnableToAllocateDelegateInfo); |
1392 | 20 | (void) memset(delegate_info,0,sizeof(DelegateInfo)); |
1393 | 20 | delegate_info->path=AcquireString(path); |
1394 | 20 | delegate_info->signature=MagickSignature; |
1395 | 20 | if (delegate_list == (DelegateInfo *) NULL) |
1396 | 20 | { |
1397 | 20 | delegate_list=delegate_info; |
1398 | 20 | continue; |
1399 | 20 | } |
1400 | 0 | delegate_list->next=delegate_info; |
1401 | 0 | delegate_info->previous=delegate_list; |
1402 | 0 | delegate_list=delegate_list->next; |
1403 | 0 | continue; |
1404 | 20 | } |
1405 | 180 | if (delegate_list == (DelegateInfo *) NULL) |
1406 | 140 | continue; |
1407 | 40 | MagickGetToken(q,(char **) NULL,token,token_max_length); |
1408 | 40 | if (*token != '=') |
1409 | 20 | continue; |
1410 | 20 | MagickGetToken(q,&q,token,token_max_length); |
1411 | 20 | MagickGetToken(q,&q,token,token_max_length); |
1412 | 20 | switch (*keyword) |
1413 | 20 | { |
1414 | 0 | case 'C': |
1415 | 0 | case 'c': |
1416 | 0 | { |
1417 | 0 | if (LocaleCompare((char *) keyword,"command") == 0) |
1418 | 0 | { |
1419 | 0 | delegate_list->commands=AllocateString(token); |
1420 | | /* |
1421 | | Support XML predefined entities substitutions. |
1422 | | |
1423 | | FIXME: Support XML character reference syntax and |
1424 | | provide more optimized support for XML predefined |
1425 | | entities substitutions. |
1426 | | */ |
1427 | 0 | if (strchr(delegate_list->commands,'&') != (char *) NULL) |
1428 | 0 | { |
1429 | 0 | SubstituteString((char **) &delegate_list->commands,"<","<"); |
1430 | 0 | SubstituteString((char **) &delegate_list->commands,">",">"); |
1431 | 0 | SubstituteString((char **) &delegate_list->commands,"'","'"); |
1432 | 0 | SubstituteString((char **) &delegate_list->commands,""","\""); |
1433 | 0 | SubstituteString((char **) &delegate_list->commands,"&","&"); |
1434 | 0 | } |
1435 | | #if defined(MSWINDOWS) |
1436 | | if (strchr(delegate_list->commands,'@') != (char *) NULL) |
1437 | | { |
1438 | | char |
1439 | | BinPath[MaxTextExtent], |
1440 | | path[MaxTextExtent]; |
1441 | | |
1442 | | BinPath[0]=0; |
1443 | | #if defined(HasGS) |
1444 | | /* Substitute @PSDelegate@ with path to Ghostscript */ |
1445 | | NTGhostscriptEXE(path,MaxTextExtent-1); |
1446 | | SubstituteString((char **) &delegate_list->commands, |
1447 | | "@PSDelegate@",path); |
1448 | | #endif /* if defined(HasGS) */ |
1449 | | |
1450 | | # if defined(UseInstalledMagick) |
1451 | | # if defined(MagickBinPath) |
1452 | | strlcpy(BinPath,MagickBinPath,sizeof(BinPath)); |
1453 | | # else |
1454 | | { |
1455 | | char |
1456 | | *key, |
1457 | | *key_value; |
1458 | | |
1459 | | /* Obtain installation path from registry */ |
1460 | | key="BinPath"; |
1461 | | key_value=NTRegistryKeyLookup(key); |
1462 | | if (!key_value) |
1463 | | { |
1464 | | ThrowException(exception,ConfigureError, |
1465 | | RegistryKeyLookupFailed,key); |
1466 | | } |
1467 | | else |
1468 | | { |
1469 | | strlcpy(BinPath,key_value,sizeof(BinPath)); |
1470 | | MagickFreeMemory(key_value); |
1471 | | } |
1472 | | } |
1473 | | # endif /* defined(MagickBinPath) */ |
1474 | | # else |
1475 | | /* Base path off of client path */ |
1476 | | strlcpy(BinPath,SetClientPath(NULL),sizeof(BinPath)); |
1477 | | # endif /* defined(UseInstalledMagick) */ |
1478 | | if ((BinPath[0] != 0) && |
1479 | | (BinPath[strlen(BinPath)-1] != *DirectorySeparator)) |
1480 | | strlcat(BinPath,DirectorySeparator,sizeof(BinPath)); |
1481 | | |
1482 | | /* Substitute @GMDelegate@ with path to gm.exe */ |
1483 | | CatDelegatePath(path,BinPath,"gm.exe"); |
1484 | | SubstituteString((char **) &delegate_list->commands, |
1485 | | "@GMDelegate@",path); |
1486 | | |
1487 | | /* Substitute @GMDisplayDelegate@ with path to |
1488 | | gmdisplay.exe */ |
1489 | | CatDelegatePath(path,BinPath,"gmdisplay.exe"); |
1490 | | SubstituteString((char **) &delegate_list->commands, |
1491 | | "@GMDisplayDelegate@",path); |
1492 | | |
1493 | | /* Substitute @MPEGDecodeDelegate@ with path to |
1494 | | mpeg2dec.exe */ |
1495 | | CatDelegatePath(path,BinPath,"mpeg2dec.exe"); |
1496 | | SubstituteString((char **) &delegate_list->commands, |
1497 | | "@MPEGDecodeDelegate@",path); |
1498 | | |
1499 | | /* Substitute @MPEGEncodeDelegate@ with path to |
1500 | | mpeg2enc.exe */ |
1501 | | CatDelegatePath(path,BinPath,"mpeg2enc.exe"); |
1502 | | SubstituteString((char **) &delegate_list->commands, |
1503 | | "@MPEGEncodeDelegate@",path); |
1504 | | |
1505 | | /* Substitute @HPGLDecodeDelegate@ with path to |
1506 | | hp2xx.exe */ |
1507 | | CatDelegatePath(path,BinPath,"hp2xx.exe"); |
1508 | | SubstituteString((char **) &delegate_list->commands, |
1509 | | "@HPGLDecodeDelegate@",path); |
1510 | | } |
1511 | | #endif /* defined(MSWINDOWS) */ |
1512 | 0 | } /* LocaleCompare */ |
1513 | 0 | break; |
1514 | 0 | } |
1515 | 0 | case 'D': |
1516 | 0 | case 'd': |
1517 | 0 | { |
1518 | 0 | if (LocaleCompare((char *) keyword,"decode") == 0) |
1519 | 0 | { |
1520 | 0 | delegate_list->decode=AcquireString(token); |
1521 | 0 | delegate_list->mode=1; |
1522 | 0 | break; |
1523 | 0 | } |
1524 | 0 | break; |
1525 | 0 | } |
1526 | 0 | case 'E': |
1527 | 0 | case 'e': |
1528 | 0 | { |
1529 | 0 | if (LocaleCompare((char *) keyword,"encode") == 0) |
1530 | 0 | { |
1531 | 0 | delegate_list->encode=AcquireString(token); |
1532 | 0 | delegate_list->mode=(-1); |
1533 | 0 | break; |
1534 | 0 | } |
1535 | 0 | break; |
1536 | 0 | } |
1537 | 0 | case 'M': |
1538 | 0 | case 'm': |
1539 | 0 | { |
1540 | 0 | if (LocaleCompare((char *) keyword,"mode") == 0) |
1541 | 0 | { |
1542 | 0 | delegate_list->mode=1; |
1543 | 0 | if (LocaleCompare(token,"bi") == 0) |
1544 | 0 | delegate_list->mode=0; |
1545 | 0 | else |
1546 | 0 | if (LocaleCompare(token,"encode") == 0) |
1547 | 0 | delegate_list->mode=(-1); |
1548 | 0 | break; |
1549 | 0 | } |
1550 | 0 | break; |
1551 | 0 | } |
1552 | 0 | case 'S': |
1553 | 20 | case 's': |
1554 | 20 | { |
1555 | 20 | if (LocaleCompare((char *) keyword,"stealth") == 0) |
1556 | 20 | { |
1557 | 20 | delegate_list->stealth=LocaleCompare(token,"True") == 0; |
1558 | 20 | break; |
1559 | 20 | } |
1560 | 0 | break; |
1561 | 20 | } |
1562 | 0 | default: |
1563 | 0 | break; |
1564 | 20 | } |
1565 | 20 | } |
1566 | 20 | MagickFreeMemory(token); |
1567 | 20 | MagickFreeMemory(xml); |
1568 | 20 | if (delegate_list == (DelegateInfo *) NULL) |
1569 | 0 | return(False); |
1570 | 20 | while (delegate_list->previous != (DelegateInfo *) NULL) |
1571 | 0 | delegate_list=delegate_list->previous; |
1572 | 20 | return(True); |
1573 | 20 | } |
1574 | | |
1575 | | /* |
1576 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1577 | | % % |
1578 | | % % |
1579 | | % % |
1580 | | % S e t D e l e g a t e I n f o % |
1581 | | % % |
1582 | | % % |
1583 | | % % |
1584 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1585 | | % |
1586 | | % Method SetDelegateInfo adds or replaces a delegate in the delegate list and |
1587 | | % returns the address of the first delegate. If the delegate is NULL, just |
1588 | | % the address of the first delegate is returned. |
1589 | | % |
1590 | | % The format of the SetDelegateInfo method is: |
1591 | | % |
1592 | | % DelegateInfo *SetDelegateInfo(DelegateInfo *delegate_info) |
1593 | | % |
1594 | | % A description of each parameter follows: |
1595 | | % |
1596 | | % o delegate_info: Method SetDelegateInfo returns the address of the |
1597 | | % first delegate in the delegates list. |
1598 | | % |
1599 | | % o delegate_info: A structure of type DelegateInfo. This information |
1600 | | % is added to the end of the delegates linked-list. |
1601 | | % |
1602 | | % |
1603 | | */ |
1604 | | MagickExport DelegateInfo *SetDelegateInfo(DelegateInfo *delegate_info) |
1605 | 0 | { |
1606 | 0 | register DelegateInfo |
1607 | 0 | *p; |
1608 | |
|
1609 | 0 | DelegateInfo |
1610 | 0 | *delegate; |
1611 | | |
1612 | | /* |
1613 | | Initialize new delegate. |
1614 | | */ |
1615 | 0 | assert(delegate_info != (DelegateInfo *) NULL); |
1616 | 0 | assert(delegate_info->signature == MagickSignature); |
1617 | 0 | delegate=MagickAllocateMemory(DelegateInfo *,sizeof(DelegateInfo)); |
1618 | 0 | if (delegate == (DelegateInfo *) NULL) |
1619 | 0 | return((DelegateInfo *) delegate_list); |
1620 | 0 | delegate->decode=AcquireString(delegate_info->decode); |
1621 | 0 | delegate->encode=AcquireString(delegate_info->encode); |
1622 | 0 | delegate->mode=delegate_info->mode; |
1623 | 0 | delegate->commands=(char *) NULL; |
1624 | 0 | if (delegate_info->commands != (char *) NULL) |
1625 | 0 | delegate->commands=AllocateString(delegate_info->commands); |
1626 | 0 | delegate->previous=(DelegateInfo *) NULL; |
1627 | 0 | delegate->next=(DelegateInfo *) NULL; |
1628 | 0 | if (delegate_list == (DelegateInfo *) NULL) |
1629 | 0 | { |
1630 | 0 | delegate_list=delegate; |
1631 | 0 | return((DelegateInfo *) delegate_list); |
1632 | 0 | } |
1633 | 0 | for (p=delegate_list; p != (DelegateInfo *) NULL; p=p->next) |
1634 | 0 | { |
1635 | 0 | if ((LocaleCompare(p->decode,delegate_info->decode) == 0) && |
1636 | 0 | (LocaleCompare(p->encode,delegate_info->encode) == 0) && |
1637 | 0 | (p->mode == delegate_info->mode)) |
1638 | 0 | { |
1639 | | /* |
1640 | | Delegate overrides an existing one with the same tags. |
1641 | | */ |
1642 | 0 | MagickFreeMemory(p->commands); |
1643 | 0 | p->commands=delegate->commands; |
1644 | 0 | MagickFreeMemory(delegate); |
1645 | 0 | return((DelegateInfo *) delegate_list); |
1646 | 0 | } |
1647 | 0 | if (p->next == (DelegateInfo *) NULL) |
1648 | 0 | break; |
1649 | 0 | } |
1650 | | /* |
1651 | | Place new delegate at the end of the delegate list. |
1652 | | */ |
1653 | 0 | delegate->previous=p; |
1654 | 0 | p->next=delegate; |
1655 | 0 | return((DelegateInfo *) delegate_list); |
1656 | 0 | } |