]> granicus.if.org Git - imagemagick/blob - MagickCore/delegate.c
(no commit message)
[imagemagick] / MagickCore / delegate.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %           DDDD   EEEEE  L      EEEEE   GGGG   AAA   TTTTT  EEEEE            %
6 %           D   D  E      L      E      G      A   A    T    E                %
7 %           D   D  EEE    L      EEE    G  GG  AAAAA    T    EEE              %
8 %           D   D  E      L      E      G   G  A   A    T    E                %
9 %           DDDD   EEEEE  LLLLL  EEEEE   GGG   A   A    T    EEEEE            %
10 %                                                                             %
11 %                                                                             %
12 %             MagickCore Methods to Read/Write/Invoke Delegates               %
13 %                                                                             %
14 %                             Software Design                                 %
15 %                                  Cristy                                     %
16 %                               October 1998                                  %
17 %                                                                             %
18 %                                                                             %
19 %  Copyright 1999-2014 ImageMagick Studio LLC, a non-profit organization      %
20 %  dedicated to making software imaging solutions freely available.           %
21 %                                                                             %
22 %  You may not use this file except in compliance with the License.  You may  %
23 %  obtain a copy of the License at                                            %
24 %                                                                             %
25 %    http://www.imagemagick.org/script/license.php                            %
26 %                                                                             %
27 %  Unless required by applicable law or agreed to in writing, software        %
28 %  distributed under the License is distributed on an "AS IS" BASIS,          %
29 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
30 %  See the License for the specific language governing permissions and        %
31 %  limitations under the License.                                             %
32 %                                                                             %
33 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
34 %
35 %  The Delegates methods associate a set of commands with a particular
36 %  image format.  ImageMagick uses delegates for formats it does not handle
37 %  directly.
38 %
39 %  Thanks to Bob Friesenhahn for the initial inspiration and design of the
40 %  delegates methods.
41 %
42 %
43 */
44 \f
45 /*
46   Include declarations.
47 */
48 #include "MagickCore/studio.h"
49 #include "MagickCore/property.h"
50 #include "MagickCore/blob.h"
51 #include "MagickCore/client.h"
52 #include "MagickCore/configure.h"
53 #include "MagickCore/constitute.h"
54 #include "MagickCore/delegate.h"
55 #include "MagickCore/delegate-private.h"
56 #include "MagickCore/exception.h"
57 #include "MagickCore/exception-private.h"
58 #include "MagickCore/hashmap.h"
59 #include "MagickCore/list.h"
60 #include "MagickCore/memory_.h"
61 #include "MagickCore/nt-base-private.h"
62 #include "MagickCore/policy.h"
63 #include "MagickCore/resource_.h"
64 #include "MagickCore/semaphore.h"
65 #include "MagickCore/string_.h"
66 #include "MagickCore/token.h"
67 #include "MagickCore/utility.h"
68 #include "MagickCore/utility-private.h"
69 #include "MagickCore/xml-tree.h"
70 #include "MagickCore/xml-tree-private.h"
71 \f
72 /*
73   Define declarations.
74 */
75 #define DelegateFilename  "delegates.xml"
76 \f
77 /*
78   Declare delegate map.
79 */
80 static const char
81   *DelegateMap = (const char *)
82     "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
83     "<delegatemap>"
84     "  <delegate decode=\"autotrace\" stealth=\"True\" command=\"&quot;autotrace&quot; -output-format svg -output-file &quot;%o&quot; &quot;%i&quot;\"/>"
85     "  <delegate decode=\"avi:decode\" stealth=\"True\" command=\"&quot;mplayer&quot; &quot;%i&quot; -really-quiet -ao null -vo png:z=3\"/>"
86     "  <delegate decode=\"browse\" stealth=\"True\" spawn=\"True\" command=\"&quot;xdg-open&quot; http://www.imagemagick.org/; rm &quot;%i&quot;\"/>"
87     "  <delegate decode=\"cgm\" thread-support=\"False\" command=\"&quot;ralcgm&quot; -d ps -oC &lt; &quot;%i&quot; &gt; &quot;%o&quot; 2&gt; &quot;%u&quot;\"/>"
88     "  <delegate decode=\"dng:decode\" command=\"&quot;/usr/bin/ufraw-batch&quot; --silent --wb=camera --black-point=auto --exposure=auto --create-id=also --out-type=ppm16 &quot;--output=%u.pnm&quot; &quot;%i&quot;\"/>"
89     "  <delegate decode=\"edit\" stealth=\"True\" command=\"&quot;xterm&quot; -title &quot;Edit Image Comment&quot; -e vi &quot;%o&quot;\"/>"
90     "  <delegate decode=\"eps\" encode=\"pdf\" mode=\"bi\" command=\"&quot;gs&quot; -q -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 &quot;-sDEVICE=pdfwrite&quot; &quot;-sOutputFile=%o&quot; &quot;-f%i&quot;\"/>"
91     "  <delegate decode=\"eps\" encode=\"ps\" mode=\"bi\" command=\"&quot;gs&quot; -q -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 &quot;-sDEVICE=pswrite&quot; &quot;-sOutputFile=%o&quot; &quot;-f%i&quot;\"/>"
92     "  <delegate decode=\"fig\" command=\"&quot;fig2dev&quot; -L ps &quot;%i&quot; &quot;%o&quot;\"/>"
93     "  <delegate decode=\"gplt\" command=\"&quot;echo&quot; &quot;set size 1.25,0.62     set terminal postscript portrait color solid; set output &quot;%o&quot;; load &quot;%i&quot;&quot; &gt; &quot;%u&quot;;&quot;gnuplot&quot; &quot;%u&quot;\"/>"
94     "  <delegate decode=\"hpg\" command=\"&quot;hp2xx&quot; -q -m eps -f `basename &quot;%o&quot;` &quot;%i&quot;     mv -f `basename &quot;%o&quot;` &quot;%o&quot;\"/>"
95     "  <delegate decode=\"hpgl\" command=\"&quot;hp2xx&quot; -q -m eps -f `basename &quot;%o&quot;` &quot;%i&quot;     mv -f `basename &quot;%o&quot;` &quot;%o&quot;\"/>"
96     "  <delegate decode=\"htm\" command=\"&quot;html2ps&quot; -U -o &quot;%o&quot; &quot;%i&quot;\"/>"
97     "  <delegate decode=\"html\" command=\"&quot;html2ps&quot; -U -o &quot;%o&quot; &quot;%i&quot;\"/>"
98     "  <delegate decode=\"https\" command=\"&quot;wget&quot; -q -O &quot;%o&quot; &quot;https:%M&quot;\"/>"
99     "  <delegate decode=\"ilbm\" command=\"&quot;ilbmtoppm&quot; &quot;%i&quot; &gt; &quot;%o&quot;\"/>"
100     "  <delegate decode=\"man\" command=\"&quot;groff&quot; -man -Tps &quot;%i&quot; &gt; &quot;%o&quot;\"/>"
101     "  <delegate decode=\"mpeg:decode\" stealth=\"True\" command=\"&quot;ffmpeg&quot; -v -1 -vframes %S -i &quot;%i&quot; -vcodec pam -an -f rawvideo -y &quot;%u.pam&quot; 2&gt; &quot;%Z&quot;\"/>"
102     "  <delegate decode=\"null\" encode=\"mpeg:encode\" stealth=\"True\" command=\"&quot;ffmpeg&quot; -v -1 -mbd rd -trellis 2 -cmp 2 -subcmp 2 -g 300 -i &quot;%M%%d.jpg&quot; &quot;%u.%m&quot; 2&gt; &quot;%Z&quot;\"/>"
103     "  <delegate decode=\"pcl:color\" stealth=\"True\" command=\"&quot;pcl6&quot; -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 &quot;-sDEVICE=ppmraw&quot; -dTextAlphaBits=%u -dGraphicsAlphaBits=%u &quot;-r%s&quot; %s &quot;-sOutputFile=%s&quot; &quot;%s&quot;\"/>"
104     "  <delegate decode=\"pcl:cmyk\" stealth=\"True\" command=\"&quot;pcl6&quot; -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 &quot;-sDEVICE=bmpsep8&quot; -dTextAlphaBits=%u -dGraphicsAlphaBits=%u &quot;-r%s&quot; %s &quot;-sOutputFile=%s&quot; &quot;%s&quot;\"/>"
105     "  <delegate decode=\"pcl:mono\" stealth=\"True\" command=\"&quot;pcl6&quot; -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 &quot;-sDEVICE=pbmraw&quot; -dTextAlphaBits=%u -dGraphicsAlphaBits=%u &quot;-r%s&quot; %s &quot;-sOutputFile=%s&quot; &quot;%s&quot;\"/>"
106     "  <delegate decode=\"pdf\" encode=\"eps\" mode=\"bi\" command=\"&quot;gs&quot; -q -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 &quot;-sDEVICE=epswrite&quot; &quot;-sOutputFile=%o&quot; &quot;-f%i&quot;\"/>"
107     "  <delegate decode=\"pdf\" encode=\"ps\" mode=\"bi\" command=\"&quot;gs&quot; -q -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 &quot;-sDEVICE=pswrite&quot; &quot;-sOutputFile=%o&quot; &quot;-f%i&quot;\"/>"
108     "  <delegate decode=\"pnm\" encode=\"ilbm\" mode=\"encode\" command=\"&quot;ppmtoilbm&quot; -24if &quot;%i&quot; &gt; &quot;%o&quot;\"/>"
109     "  <delegate decode=\"pnm\" encode=\"launch\" mode=\"encode\" command=\"&quot;gimp&quot; &quot;%i&quot;\"/>"
110     "  <delegate decode=\"pov\" command=\"&quot;povray&quot; &quot;+i&quot;%i&quot;&quot; -D0 +o&quot;%o&quot; +fn%q +w%w +h%h +a -q9 -kfi&quot;%s&quot; -kff&quot;%n&quot;     &quot;convert&quot; -concatenate &quot;%o*.png&quot; &quot;%o&quot;\"/>"
111     "  <delegate decode=\"ps\" encode=\"eps\" mode=\"bi\" command=\"&quot;gs&quot; -q -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 &quot;-sDEVICE=epswrite&quot; &quot;-sOutputFile=%o&quot; &quot;-f%i&quot;\"/>"
112     "  <delegate decode=\"ps\" encode=\"pdf\" mode=\"bi\" command=\"&quot;gs&quot; -q -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 &quot;-sDEVICE=pdfwrite&quot; &quot;-sOutputFile=%o&quot; &quot;-f%i&quot;\"/>"
113     "  <delegate decode=\"ps\" encode=\"print\" mode=\"encode\" command=\"lpr &quot;%i&quot;\"/>"
114     "  <delegate decode=\"ps:alpha\" stealth=\"True\" command=\"&quot;gs&quot; -q -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 &quot;-sDEVICE=pngalpha&quot; -dTextAlphaBits=%u -dGraphicsAlphaBits=%u &quot;-r%s&quot; %s &quot;-sOutputFile=%s&quot; &quot;-f%s&quot; &quot;-f%s&quot;\"/>"
115     "  <delegate decode=\"ps:bbox\" stealth=\"True\" command=\"&quot;gs&quot; -q -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 &quot;-sDEVICE=bbox&quot; -dTextAlphaBits=%u -dGraphicsAlphaBits=%u &quot;-r%s&quot; %s &quot;-sOutputFile=%s&quot; &quot;-f%s&quot; &quot;-f%s&quot;\"/>"
116     "  <delegate decode=\"ps:cmyk\" stealth=\"True\" command=\"&quot;gs&quot; -q -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 &quot;-sDEVICE=bmpsep8&quot; -dTextAlphaBits=%u -dGraphicsAlphaBits=%u &quot;-r%s&quot; %s &quot;-sOutputFile=%s&quot; &quot;-f%s&quot; &quot;-f%s&quot;\"/>"
117     "  <delegate decode=\"ps:color\" stealth=\"True\" command=\"&quot;gs&quot; -q -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 &quot;-sDEVICE=pnmraw&quot; -dTextAlphaBits=%u -dGraphicsAlphaBits=%u &quot;-r%s&quot; %s &quot;-sOutputFile=%s&quot; &quot;-f%s&quot; &quot;-f%s&quot;\"/>"
118     "  <delegate decode=\"ps:mono\" stealth=\"True\" command=\"&quot;gs&quot; -q -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 &quot;-sDEVICE=pnmraw&quot; -dTextAlphaBits=%u -dGraphicsAlphaBits=%u &quot;-r%s&quot; %s &quot;-sOutputFile=%s&quot; &quot;-f%s&quot; &quot;-f%s&quot;\"/>"
119     "  <delegate decode=\"rgba\" encode=\"rle\" mode=\"encode\" command=\"&quot;rawtorle&quot; -o &quot;%o&quot; -v &quot;%i&quot;\"/>"
120     "  <delegate decode=\"scan\" command=\"&quot;scanimage&quot; -d &quot;%i&quot; &gt; &quot;%o&quot;\"/>"
121     "  <delegate encode=\"show\" spawn=\"True\" command=\"&quot;display&quot; -immutable -delay 0 -window-group %g -title &quot;%l of %f&quot; &quot;temporary:%i&quot;\"/>"
122     "  <delegate decode=\"shtml\" command=\"&quot;html2ps&quot; -U -o &quot;%o&quot; &quot;%i&quot;\"/>"
123     "  <delegate decode=\"svg\" command=\"&quot;rsvg&quot; &quot;%i&quot; &quot;%o&quot;\"/>"
124     "  <delegate decode=\"txt\" encode=\"ps\" mode=\"bi\" command=\"&quot;enscript&quot; -o &quot;%o&quot; &quot;%i&quot;\"/>"
125     "  <delegate encode=\"win\" stealth=\"True\" spawn=\"True\" command=\"&quot;display&quot; -immutable -delay 0 -window-group %g -title &quot;%l of %f&quot; &quot;temporary:%i&quot;\"/>"
126     "  <delegate decode=\"wmf\" command=\"&quot;wmf2eps&quot; -o &quot;%o&quot; &quot;%i&quot;\"/>"
127     "</delegatemap>";
128 \f
129 /*
130   Global declaractions.
131 */
132 static LinkedListInfo
133   *delegate_cache = (LinkedListInfo *) NULL;
134
135 static SemaphoreInfo
136   *delegate_semaphore = (SemaphoreInfo *) NULL;
137 \f
138 /*
139   Forward declaractions.
140 */
141 static MagickBooleanType
142   IsDelegateCacheInstantiated(ExceptionInfo *),
143   LoadDelegateCache(LinkedListInfo *,const char *,const char *,const size_t,
144     ExceptionInfo *);
145 \f
146 /*
147 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
148 %                                                                             %
149 %                                                                             %
150 %                                                                             %
151 %  A c q u i r e D e l e g a t e C a c h e                                    %
152 %                                                                             %
153 %                                                                             %
154 %                                                                             %
155 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
156 %
157 %  AcquireDelegateCache() caches one or more delegate configurations which
158 %  provides a mapping between delegate attributes and a delegate name.
159 %
160 %  The format of the AcquireDelegateCache method is:
161 %
162 %      LinkedListInfo *AcquireDelegateCache(const char *filename,
163 %        ExceptionInfo *exception)
164 %
165 %  A description of each parameter follows:
166 %
167 %    o filename: the font file name.
168 %
169 %    o exception: return any errors or warnings in this structure.
170 %
171 */
172 static LinkedListInfo *AcquireDelegateCache(const char *filename,
173   ExceptionInfo *exception)
174 {
175   LinkedListInfo
176     *delegate_cache;
177
178   MagickStatusType
179     status;
180
181   delegate_cache=NewLinkedList(0);
182   if (delegate_cache == (LinkedListInfo *) NULL)
183     ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
184   status=MagickTrue;
185 #if !defined(MAGICKCORE_ZERO_CONFIGURATION_SUPPORT)
186   {
187     const StringInfo
188       *option;
189
190     LinkedListInfo
191       *options;
192
193     options=GetConfigureOptions(filename,exception);
194     option=(const StringInfo *) GetNextValueInLinkedList(options);
195     while (option != (const StringInfo *) NULL)
196     {
197       status&=LoadDelegateCache(delegate_cache,(const char *)
198         GetStringInfoDatum(option),GetStringInfoPath(option),0,exception);
199       option=(const StringInfo *) GetNextValueInLinkedList(options);
200     }
201     options=DestroyConfigureOptions(options);
202   }
203 #endif
204   if (IfMagickTrue(IsLinkedListEmpty(delegate_cache)))
205     status&=LoadDelegateCache(delegate_cache,DelegateMap,"built-in",0,
206       exception);
207   return(delegate_cache);
208 }
209 \f
210 /*
211 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
212 %                                                                             %
213 %                                                                             %
214 %                                                                             %
215 +   D e l e g a t e C o m p o n e n t G e n e s i s                           %
216 %                                                                             %
217 %                                                                             %
218 %                                                                             %
219 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
220 %
221 %  DelegateComponentGenesis() instantiates the delegate component.
222 %
223 %  The format of the DelegateComponentGenesis method is:
224 %
225 %      MagickBooleanType DelegateComponentGenesis(void)
226 %
227 */
228 MagickPrivate MagickBooleanType DelegateComponentGenesis(void)
229 {
230   if (delegate_semaphore == (SemaphoreInfo *) NULL)
231     delegate_semaphore=AcquireSemaphoreInfo();
232   return(MagickTrue);
233 }
234 \f
235 /*
236 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
237 %                                                                             %
238 %                                                                             %
239 %                                                                             %
240 %   D e l e g a t e C o m p o n e n t T e r m i n u s                         %
241 %                                                                             %
242 %                                                                             %
243 %                                                                             %
244 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
245 %
246 %  DelegateComponentTerminus() destroys the delegate component.
247 %
248 %  The format of the DelegateComponentTerminus method is:
249 %
250 %      DelegateComponentTerminus(void)
251 %
252 */
253
254 static void *DestroyDelegate(void *delegate_info)
255 {
256   register DelegateInfo
257     *p;
258
259   p=(DelegateInfo *) delegate_info;
260   if (p->path != (char *) NULL)
261     p->path=DestroyString(p->path);
262   if (p->decode != (char *) NULL)
263     p->decode=DestroyString(p->decode);
264   if (p->encode != (char *) NULL)
265     p->encode=DestroyString(p->encode);
266   if (p->commands != (char *) NULL)
267     p->commands=DestroyString(p->commands);
268   if (p->semaphore != (SemaphoreInfo *) NULL)
269     RelinquishSemaphoreInfo(&p->semaphore);
270   p=(DelegateInfo *) RelinquishMagickMemory(p);
271   return((void *) NULL);
272 }
273
274 MagickPrivate void DelegateComponentTerminus(void)
275 {
276   if (delegate_semaphore == (SemaphoreInfo *) NULL)
277     ActivateSemaphoreInfo(&delegate_semaphore);
278   LockSemaphoreInfo(delegate_semaphore);
279   if (delegate_cache != (LinkedListInfo *) NULL)
280     delegate_cache=DestroyLinkedList(delegate_cache,DestroyDelegate);
281   UnlockSemaphoreInfo(delegate_semaphore);
282   RelinquishSemaphoreInfo(&delegate_semaphore);
283 }
284 \f
285 /*
286 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
287 %                                                                             %
288 %                                                                             %
289 %                                                                             %
290 %   G e t D e l e g a t e C o m m a n d                                       %
291 %                                                                             %
292 %                                                                             %
293 %                                                                             %
294 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
295 %
296 %  GetDelegateCommand() replaces any embedded formatting characters with the
297 %  appropriate image attribute and returns the resulting command.
298 %
299 %  The format of the GetDelegateCommand method is:
300 %
301 %      char *GetDelegateCommand(const ImageInfo *image_info,Image *image,
302 %        const char *decode,const char *encode,ExceptionInfo *exception)
303 %
304 %  A description of each parameter follows:
305 %
306 %    o command: Method GetDelegateCommand returns the command associated
307 %      with specified delegate tag.
308 %
309 %    o image_info: the image info.
310 %
311 %    o image: the image.
312 %
313 %    o decode: Specifies the decode delegate we are searching for as a
314 %      character string.
315 %
316 %    o encode: Specifies the encode delegate we are searching for as a
317 %      character string.
318 %
319 %    o exception: return any errors or warnings in this structure.
320 %
321 */
322 MagickExport char *GetDelegateCommand(const ImageInfo *image_info,Image *image,
323   const char *decode,const char *encode,ExceptionInfo *exception)
324 {
325   char
326     *command,
327     **commands;
328
329   const DelegateInfo
330     *delegate_info;
331
332   register ssize_t
333     i;
334
335   assert(image_info != (ImageInfo *) NULL);
336   assert(image_info->signature == MagickSignature);
337   assert(image != (Image *) NULL);
338   assert(image->signature == MagickSignature);
339   if( IfMagickTrue(image->debug) )
340     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
341
342   delegate_info=GetDelegateInfo(decode,encode,exception);
343   if (delegate_info == (const DelegateInfo *) NULL)
344     {
345       (void) ThrowMagickException(exception,GetMagickModule(),DelegateError,
346         "NoTagFound","`%s'",decode ? decode : encode);
347       return((char *) NULL);
348     }
349   commands=StringToList(delegate_info->commands);
350   if (commands == (char **) NULL)
351     {
352       (void) ThrowMagickException(exception,GetMagickModule(),
353         ResourceLimitError,"MemoryAllocationFailed","`%s'",decode ? decode :
354         encode);
355       return((char *) NULL);
356     }
357   command=InterpretImageProperties((ImageInfo *) image_info,image,commands[0],
358     exception);
359   if (command == (char *) NULL)
360     (void) ThrowMagickException(exception,GetMagickModule(),ResourceLimitError,
361       "MemoryAllocationFailed","`%s'",commands[0]);
362   /*
363     Relinquish resources.
364   */
365   for (i=0; commands[i] != (char *) NULL; i++)
366     commands[i]=DestroyString(commands[i]);
367   commands=(char **) RelinquishMagickMemory(commands);
368   return(command);
369 }
370 \f
371 /*
372 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
373 %                                                                             %
374 %                                                                             %
375 %                                                                             %
376 %   G e t D e l e g a t e C o m m a n d s                                     %
377 %                                                                             %
378 %                                                                             %
379 %                                                                             %
380 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
381 %
382 %  GetDelegateCommands() returns the commands associated with a delegate.
383 %
384 %  The format of the GetDelegateCommands method is:
385 %
386 %      const char *GetDelegateCommands(const DelegateInfo *delegate_info)
387 %
388 %  A description of each parameter follows:
389 %
390 %    o delegate_info:  The delegate info.
391 %
392 */
393 MagickExport const char *GetDelegateCommands(const DelegateInfo *delegate_info)
394 {
395   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
396
397   assert(delegate_info != (DelegateInfo *) NULL);
398   assert(delegate_info->signature == MagickSignature);
399   return(delegate_info->commands);
400 }
401 \f
402 /*
403 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
404 %                                                                             %
405 %                                                                             %
406 %                                                                             %
407 %   G e t D e l e g a t e I n f o                                             %
408 %                                                                             %
409 %                                                                             %
410 %                                                                             %
411 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
412 %
413 %  GetDelegateInfo() returns any delegates associated with the specified tag.
414 %
415 %  The format of the GetDelegateInfo method is:
416 %
417 %      const DelegateInfo *GetDelegateInfo(const char *decode,
418 %        const char *encode,ExceptionInfo *exception)
419 %
420 %  A description of each parameter follows:
421 %
422 %    o decode: Specifies the decode delegate we are searching for as a
423 %      character string.
424 %
425 %    o encode: Specifies the encode delegate we are searching for as a
426 %      character string.
427 %
428 %    o exception: return any errors or warnings in this structure.
429 %
430 */
431 MagickExport const DelegateInfo *GetDelegateInfo(const char *decode,
432   const char *encode,ExceptionInfo *exception)
433 {
434   register const DelegateInfo
435     *p;
436
437   assert(exception != (ExceptionInfo *) NULL);
438   if (IfMagickFalse(IsDelegateCacheInstantiated(exception)))
439     return((const DelegateInfo *) NULL);
440   /*
441     Search for named delegate.
442   */
443   LockSemaphoreInfo(delegate_semaphore);
444   ResetLinkedListIterator(delegate_cache);
445   p=(const DelegateInfo *) GetNextValueInLinkedList(delegate_cache);
446   if ((LocaleCompare(decode,"*") == 0) && (LocaleCompare(encode,"*") == 0))
447     {
448       UnlockSemaphoreInfo(delegate_semaphore);
449       return(p);
450     }
451   while (p != (const DelegateInfo *) NULL)
452   {
453     if (p->mode > 0)
454       {
455         if (LocaleCompare(p->decode,decode) == 0)
456           break;
457         p=(const DelegateInfo *) GetNextValueInLinkedList(delegate_cache);
458         continue;
459       }
460     if (p->mode < 0)
461       {
462         if (LocaleCompare(p->encode,encode) == 0)
463           break;
464         p=(const DelegateInfo *) GetNextValueInLinkedList(delegate_cache);
465         continue;
466       }
467     if (LocaleCompare(decode,p->decode) == 0)
468       if (LocaleCompare(encode,p->encode) == 0)
469         break;
470     if (LocaleCompare(decode,"*") == 0)
471       if (LocaleCompare(encode,p->encode) == 0)
472         break;
473     if (LocaleCompare(decode,p->decode) == 0)
474       if (LocaleCompare(encode,"*") == 0)
475         break;
476     p=(const DelegateInfo *) GetNextValueInLinkedList(delegate_cache);
477   }
478   if (p != (const DelegateInfo *) NULL)
479     (void) InsertValueInLinkedList(delegate_cache,0,
480       RemoveElementByValueFromLinkedList(delegate_cache,p));
481   UnlockSemaphoreInfo(delegate_semaphore);
482   return(p);
483 }
484 \f
485 /*
486 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
487 %                                                                             %
488 %                                                                             %
489 %                                                                             %
490 %   G e t D e l e g a t e I n f o L i s t                                     %
491 %                                                                             %
492 %                                                                             %
493 %                                                                             %
494 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
495 %
496 %  GetDelegateInfoList() returns any delegates that match the specified pattern.
497 %
498 %  The delegate of the GetDelegateInfoList function is:
499 %
500 %      const DelegateInfo **GetDelegateInfoList(const char *pattern,
501 %        size_t *number_delegates,ExceptionInfo *exception)
502 %
503 %  A description of each parameter follows:
504 %
505 %    o pattern: Specifies a pointer to a text string containing a pattern.
506 %
507 %    o number_delegates:  This integer returns the number of delegates in the
508 %      list.
509 %
510 %    o exception: return any errors or warnings in this structure.
511 %
512 */
513
514 #if defined(__cplusplus) || defined(c_plusplus)
515 extern "C" {
516 #endif
517
518 static int DelegateInfoCompare(const void *x,const void *y)
519 {
520   const DelegateInfo
521     **p,
522     **q;
523
524   p=(const DelegateInfo **) x,
525   q=(const DelegateInfo **) y;
526   if (LocaleCompare((*p)->path,(*q)->path) == 0)
527     {
528       if ((*p)->decode == (char *) NULL)
529         if (((*p)->encode != (char *) NULL) &&
530             ((*q)->encode != (char *) NULL))
531           return(strcmp((*p)->encode,(*q)->encode));
532       if (((*p)->decode != (char *) NULL) &&
533           ((*q)->decode != (char *) NULL))
534         return(strcmp((*p)->decode,(*q)->decode));
535     }
536   return(LocaleCompare((*p)->path,(*q)->path));
537 }
538
539 #if defined(__cplusplus) || defined(c_plusplus)
540 }
541 #endif
542
543 MagickExport const DelegateInfo **GetDelegateInfoList(const char *pattern,
544   size_t *number_delegates,ExceptionInfo *exception)
545 {
546   const DelegateInfo
547     **delegates;
548
549   register const DelegateInfo
550     *p;
551
552   register ssize_t
553     i;
554
555   /*
556     Allocate delegate list.
557   */
558   assert(number_delegates != (size_t *) NULL);
559   assert(pattern != (char *) NULL);
560   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",pattern);
561
562   *number_delegates=0;
563   p=GetDelegateInfo("*","*",exception);
564   if (p == (const DelegateInfo *) NULL)
565     return((const DelegateInfo **) NULL);
566   delegates=(const DelegateInfo **) AcquireQuantumMemory((size_t)
567     GetNumberOfElementsInLinkedList(delegate_cache)+1UL,sizeof(*delegates));
568   if (delegates == (const DelegateInfo **) NULL)
569     return((const DelegateInfo **) NULL);
570   /*
571     Generate delegate list.
572   */
573   LockSemaphoreInfo(delegate_semaphore);
574   ResetLinkedListIterator(delegate_cache);
575   p=(const DelegateInfo *) GetNextValueInLinkedList(delegate_cache);
576   for (i=0; p != (const DelegateInfo *) NULL; )
577   {
578     if( IfMagickFalse(p->stealth) &&
579         ( IfMagickTrue(GlobExpression(p->decode,pattern,MagickFalse)) ||
580           IfMagickTrue(GlobExpression(p->encode,pattern,MagickFalse))) )
581       delegates[i++]=p;
582     p=(const DelegateInfo *) GetNextValueInLinkedList(delegate_cache);
583   }
584   UnlockSemaphoreInfo(delegate_semaphore);
585   qsort((void *) delegates,(size_t) i,sizeof(*delegates),DelegateInfoCompare);
586   delegates[i]=(DelegateInfo *) NULL;
587   *number_delegates=(size_t) i;
588   return(delegates);
589 }
590 \f
591 /*
592 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
593 %                                                                             %
594 %                                                                             %
595 %                                                                             %
596 %   G e t D e l e g a t e L i s t                                             %
597 %                                                                             %
598 %                                                                             %
599 %                                                                             %
600 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
601 %
602 %  GetDelegateList() returns any image format delegates that match the
603 %  specified  pattern.
604 %
605 %  The format of the GetDelegateList function is:
606 %
607 %      char **GetDelegateList(const char *pattern,
608 %        size_t *number_delegates,ExceptionInfo *exception)
609 %
610 %  A description of each parameter follows:
611 %
612 %    o pattern: Specifies a pointer to a text string containing a pattern.
613 %
614 %    o number_delegates:  This integer returns the number of delegates
615 %      in the list.
616 %
617 %    o exception: return any errors or warnings in this structure.
618 %
619 */
620
621 #if defined(__cplusplus) || defined(c_plusplus)
622 extern "C" {
623 #endif
624
625 static int DelegateCompare(const void *x,const void *y)
626 {
627   register const char
628     **p,
629     **q;
630
631   p=(const char **) x;
632   q=(const char **) y;
633   return(LocaleCompare(*p,*q));
634 }
635
636 #if defined(__cplusplus) || defined(c_plusplus)
637 }
638 #endif
639
640 MagickExport char **GetDelegateList(const char *pattern,
641   size_t *number_delegates,ExceptionInfo *exception)
642 {
643   char
644     **delegates;
645
646   register const DelegateInfo
647     *p;
648
649   register ssize_t
650     i;
651
652   /*
653     Allocate delegate list.
654   */
655   assert(pattern != (char *) NULL);
656   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",pattern);
657
658   assert(number_delegates != (size_t *) NULL);
659   *number_delegates=0;
660   p=GetDelegateInfo("*","*",exception);
661   if (p == (const DelegateInfo *) NULL)
662     return((char **) NULL);
663   delegates=(char **) AcquireQuantumMemory((size_t)
664     GetNumberOfElementsInLinkedList(delegate_cache)+1UL,sizeof(*delegates));
665   if (delegates == (char **) NULL)
666     return((char **) NULL);
667   LockSemaphoreInfo(delegate_semaphore);
668   ResetLinkedListIterator(delegate_cache);
669   p=(const DelegateInfo *) GetNextValueInLinkedList(delegate_cache);
670   for (i=0; p != (const DelegateInfo *) NULL; )
671   {
672     if( IfMagickFalse(p->stealth) &&
673         IfMagickTrue(GlobExpression(p->decode,pattern,MagickFalse)) )
674       delegates[i++]=ConstantString(p->decode);
675     if( IfMagickFalse(p->stealth) &&
676         IfMagickTrue(GlobExpression(p->encode,pattern,MagickFalse)) )
677       delegates[i++]=ConstantString(p->encode);
678     p=(const DelegateInfo *) GetNextValueInLinkedList(delegate_cache);
679   }
680   UnlockSemaphoreInfo(delegate_semaphore);
681   qsort((void *) delegates,(size_t) i,sizeof(*delegates),DelegateCompare);
682   delegates[i]=(char *) NULL;
683   *number_delegates=(size_t) i;
684   return(delegates);
685 }
686 \f
687 /*
688 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
689 %                                                                             %
690 %                                                                             %
691 %                                                                             %
692 %   G e t D e l e g a t e M o d e                                             %
693 %                                                                             %
694 %                                                                             %
695 %                                                                             %
696 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
697 %
698 %  GetDelegateMode() returns the mode of the delegate.
699 %
700 %  The format of the GetDelegateMode method is:
701 %
702 %      ssize_t GetDelegateMode(const DelegateInfo *delegate_info)
703 %
704 %  A description of each parameter follows:
705 %
706 %    o delegate_info:  The delegate info.
707 %
708 */
709 MagickExport ssize_t GetDelegateMode(const DelegateInfo *delegate_info)
710 {
711   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
712
713   assert(delegate_info != (DelegateInfo *) NULL);
714   assert(delegate_info->signature == MagickSignature);
715   return(delegate_info->mode);
716 }
717 \f
718 /*
719 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
720 %                                                                             %
721 %                                                                             %
722 %                                                                             %
723 +   G e t D e l e g a t e T h r e a d S u p p o r t                           %
724 %                                                                             %
725 %                                                                             %
726 %                                                                             %
727 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
728 %
729 %  GetDelegateThreadSupport() returns MagickTrue if the delegate supports
730 %  threads.
731 %
732 %  The format of the GetDelegateThreadSupport method is:
733 %
734 %      MagickBooleanType GetDelegateThreadSupport(
735 %        const DelegateInfo *delegate_info)
736 %
737 %  A description of each parameter follows:
738 %
739 %    o delegate_info:  The delegate info.
740 %
741 */
742 MagickExport MagickBooleanType GetDelegateThreadSupport(
743   const DelegateInfo *delegate_info)
744 {
745   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
746
747   assert(delegate_info != (DelegateInfo *) NULL);
748   assert(delegate_info->signature == MagickSignature);
749   return(delegate_info->thread_support);
750 }
751 \f
752 /*
753 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
754 %                                                                             %
755 %                                                                             %
756 %                                                                             %
757 +   I s D e l e g a t e C a c h e I n s t a n t i a t e d                     %
758 %                                                                             %
759 %                                                                             %
760 %                                                                             %
761 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
762 %
763 %  IsDelegateCacheInstantiated() determines if the delegate cache is
764 %  instantiated.  If not, it instantiates the cache and returns it.
765 %
766 %  The format of the IsDelegateInstantiated method is:
767 %
768 %      MagickBooleanType IsDelegateCacheInstantiated(ExceptionInfo *exception)
769 %
770 %  A description of each parameter follows.
771 %
772 %    o exception: return any errors or warnings in this structure.
773 %
774 */
775 static MagickBooleanType IsDelegateCacheInstantiated(ExceptionInfo *exception)
776 {
777   if (delegate_cache == (LinkedListInfo *) NULL)
778     {
779       if (delegate_semaphore == (SemaphoreInfo *) NULL)
780         ActivateSemaphoreInfo(&delegate_semaphore);
781       LockSemaphoreInfo(delegate_semaphore);
782       if (delegate_cache == (LinkedListInfo *) NULL)
783         delegate_cache=AcquireDelegateCache(DelegateFilename,exception);
784       UnlockSemaphoreInfo(delegate_semaphore);
785     }
786   return(IsMagickNotNULL(delegate_cache));
787 }
788 \f
789 /*
790 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
791 %                                                                             %
792 %                                                                             %
793 %                                                                             %
794 %   I n v o k e D e l e g a t e                                               %
795 %                                                                             %
796 %                                                                             %
797 %                                                                             %
798 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
799 %
800 %  InvokeDelegate replaces any embedded formatting characters with the
801 %  appropriate image attribute and executes the resulting command.  MagickFalse
802 %  is returned if the commands execute with success otherwise MagickTrue.
803 %
804 %  The format of the InvokeDelegate method is:
805 %
806 %      MagickBooleanType InvokeDelegate(ImageInfo *image_info,Image *image,
807 %        const char *decode,const char *encode,ExceptionInfo *exception)
808 %
809 %  A description of each parameter follows:
810 %
811 %    o image_info: the imageInfo.
812 %
813 %    o image: the image.
814 %
815 %    o exception: return any errors or warnings in this structure.
816 %
817 */
818
819 static inline size_t MagickMin(const size_t x,const size_t y)
820 {
821   if (x < y)
822     return(x);
823   return(y);
824 }
825
826 static MagickBooleanType CopyDelegateFile(const char *source,
827   const char *destination)
828 {
829   int
830     destination_file,
831     source_file;
832
833   MagickBooleanType
834     status;
835
836   register size_t
837     i;
838
839   size_t
840     length,
841     quantum;
842
843   ssize_t
844     count;
845
846   struct stat
847     attributes;
848
849   unsigned char
850     *buffer;
851
852   /*
853     Copy source file to destination.
854   */
855   assert(source != (const char *) NULL);
856   assert(destination != (char *) NULL);
857   status=GetPathAttributes(destination,&attributes);
858   if( IfMagickTrue(status) && (attributes.st_size != 0))
859     return(MagickTrue);
860   destination_file=open_utf8(destination,O_WRONLY | O_BINARY | O_CREAT,S_MODE);
861   if (destination_file == -1)
862     return(MagickFalse);
863   source_file=open_utf8(source,O_RDONLY | O_BINARY,0);
864   if (source_file == -1)
865     {
866       (void) close(destination_file);
867       return(MagickFalse);
868     }
869   quantum=(size_t) MagickMaxBufferExtent;
870   if ((fstat(source_file,&attributes) == 0) && (attributes.st_size != 0))
871     quantum=MagickMin((size_t) attributes.st_size,MagickMaxBufferExtent);
872   buffer=(unsigned char *) AcquireQuantumMemory(quantum,sizeof(*buffer));
873   if (buffer == (unsigned char *) NULL)
874     {
875       (void) close(source_file);
876       (void) close(destination_file);
877       return(MagickFalse);
878     }
879   length=0;
880   for (i=0; ; i+=count)
881   {
882     count=(ssize_t) read(source_file,buffer,quantum);
883     if (count <= 0)
884       break;
885     length=(size_t) count;
886     count=(ssize_t) write(destination_file,buffer,length);
887     if ((size_t) count != length)
888       break;
889   }
890   (void) close(destination_file);
891   (void) close(source_file);
892   buffer=(unsigned char *) RelinquishMagickMemory(buffer);
893   return(IsMagickTrue(i!=0));
894 }
895
896 static char *SanitizeDelegateCommand(const char *command)
897 {
898   char
899     *sanitize_command;
900
901   const char
902     *q;
903
904   register char
905     *p;
906
907   static char
908     whitelist[] =
909       "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-"
910       ".@&;<>|\\\'\":%";
911
912   sanitize_command=AcquireString(command);
913   p=sanitize_command;
914   q=sanitize_command+strlen(sanitize_command);
915   for (p+=strspn(p,whitelist); p != q; p+=strspn(p,whitelist))
916     *p='_';
917   return(sanitize_command);
918 }
919
920 MagickExport MagickBooleanType InvokeDelegate(ImageInfo *image_info,
921   Image *image,const char *decode,const char *encode,ExceptionInfo *exception)
922 {
923   char
924     *command,
925     **commands,
926     input_filename[MaxTextExtent],
927     output_filename[MaxTextExtent];
928
929   const DelegateInfo
930     *delegate_info;
931
932   MagickBooleanType
933     status,
934     temporary;
935
936   register ssize_t
937     i;
938
939   PolicyRights
940     rights;
941
942   /*
943     Get delegate.
944   */
945   assert(image_info != (ImageInfo *) NULL);
946   assert(image_info->signature == MagickSignature);
947   assert(image != (Image *) NULL);
948   assert(image->signature == MagickSignature);
949   if( IfMagickTrue(image->debug) )
950     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
951
952   rights=ExecutePolicyRights;
953   if( IfMagickFalse(IsRightsAuthorized(DelegatePolicyDomain,rights,decode)) )
954     {
955       errno=EPERM;
956       (void) ThrowMagickException(exception,GetMagickModule(),PolicyError,
957         "NotAuthorized","`%s'",decode);
958       return(MagickFalse);
959     }
960   if( IfMagickFalse(IsRightsAuthorized(DelegatePolicyDomain,rights,encode)) )
961     {
962       errno=EPERM;
963       (void) ThrowMagickException(exception,GetMagickModule(),PolicyError,
964         "NotAuthorized","`%s'",encode);
965       return(MagickFalse);
966     }
967   temporary=IsMagickTrue(*image->filename == '\0');
968   if( IfMagickTrue(temporary) )
969     if( IfMagickFalse(AcquireUniqueFilename(image->filename)) )
970       {
971         ThrowFileException(exception,FileOpenError,
972           "UnableToCreateTemporaryFile",image->filename);
973         return(MagickFalse);
974       }
975   delegate_info=GetDelegateInfo(decode,encode,exception);
976   if (delegate_info == (DelegateInfo *) NULL)
977     {
978       if( IfMagickTrue(temporary) )
979         (void) RelinquishUniqueFileResource(image->filename);
980       (void) ThrowMagickException(exception,GetMagickModule(),DelegateError,
981         "NoTagFound","`%s'",decode ? decode : encode);
982       return(MagickFalse);
983     }
984   if (*image_info->filename == '\0')
985     {
986       if( IfMagickFalse(AcquireUniqueFilename(image_info->filename)) )
987         {
988           if( IfMagickTrue(temporary) )
989             (void) RelinquishUniqueFileResource(image->filename);
990           ThrowFileException(exception,FileOpenError,
991             "UnableToCreateTemporaryFile",image_info->filename);
992           return(MagickFalse);
993         }
994       image_info->temporary=MagickTrue;
995     }
996   if ((delegate_info->mode != 0) && (((decode != (const char *) NULL) &&
997         (delegate_info->encode != (char *) NULL)) ||
998        ((encode != (const char *) NULL) &&
999         (delegate_info->decode != (char *) NULL))))
1000     {
1001       char
1002         *magick;
1003
1004       ImageInfo
1005         *clone_info;
1006
1007       register Image
1008         *p;
1009
1010       /*
1011         Delegate requires a particular image format.
1012       */
1013       if( IfMagickFalse(AcquireUniqueFilename(image_info->unique)) )
1014         {
1015           ThrowFileException(exception,FileOpenError,
1016             "UnableToCreateTemporaryFile",image_info->unique);
1017           return(MagickFalse);
1018         }
1019       if( IfMagickFalse(AcquireUniqueFilename(image_info->zero)) )
1020         {
1021           (void) RelinquishUniqueFileResource(image_info->unique);
1022           ThrowFileException(exception,FileOpenError,
1023             "UnableToCreateTemporaryFile",image_info->zero);
1024           return(MagickFalse);
1025         }
1026       magick=InterpretImageProperties(image_info,image,decode != (char *) NULL ?
1027         delegate_info->encode : delegate_info->decode,exception);
1028       if (magick == (char *) NULL)
1029         {
1030           (void) RelinquishUniqueFileResource(image_info->unique);
1031           (void) RelinquishUniqueFileResource(image_info->zero);
1032           if( IfMagickTrue(temporary) )
1033             (void) RelinquishUniqueFileResource(image->filename);
1034           (void) ThrowMagickException(exception,GetMagickModule(),
1035             DelegateError,"DelegateFailed","`%s'",decode ? decode : encode);
1036           return(MagickFalse);
1037         }
1038       LocaleUpper(magick);
1039       clone_info=CloneImageInfo(image_info);
1040       (void) CopyMagickString((char *) clone_info->magick,magick,
1041         MaxTextExtent);
1042       if (LocaleCompare(magick,"NULL") != 0)
1043         (void) CopyMagickString(image->magick,magick,MaxTextExtent);
1044       magick=DestroyString(magick);
1045       (void) FormatLocaleString(clone_info->filename,MaxTextExtent,"%s:",
1046         delegate_info->decode);
1047       (void) SetImageInfo(clone_info,(unsigned int) GetImageListLength(image),
1048         exception);
1049       (void) CopyMagickString(clone_info->filename,image_info->filename,
1050         MaxTextExtent);
1051       (void) CopyMagickString(image_info->filename,image->filename,
1052         MaxTextExtent);
1053       for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
1054       {
1055         (void) FormatLocaleString(p->filename,MaxTextExtent,"%s:%s",
1056           delegate_info->decode,clone_info->filename);
1057         status=WriteImage(clone_info,p,exception);
1058         if( IfMagickFalse(status) )
1059           {
1060             (void) RelinquishUniqueFileResource(image_info->unique);
1061             (void) RelinquishUniqueFileResource(image_info->zero);
1062             if( IfMagickTrue(temporary) )
1063               (void) RelinquishUniqueFileResource(image->filename);
1064             clone_info=DestroyImageInfo(clone_info);
1065             (void) ThrowMagickException(exception,GetMagickModule(),
1066               DelegateError,"DelegateFailed","`%s'",decode ? decode : encode);
1067             return(MagickFalse);
1068           }
1069         if( IfMagickTrue(clone_info->adjoin) )
1070           break;
1071       }
1072       (void) RelinquishUniqueFileResource(image_info->unique);
1073       (void) RelinquishUniqueFileResource(image_info->zero);
1074       clone_info=DestroyImageInfo(clone_info);
1075     }
1076   /*
1077     Invoke delegate.
1078   */
1079   commands=StringToList(delegate_info->commands);
1080   if (commands == (char **) NULL)
1081     {
1082       if( IfMagickTrue(temporary) )
1083         (void) RelinquishUniqueFileResource(image->filename);
1084       (void) ThrowMagickException(exception,GetMagickModule(),
1085         ResourceLimitError,"MemoryAllocationFailed","`%s'",
1086         decode ? decode : encode);
1087       return(MagickFalse);
1088     }
1089   command=(char *) NULL;
1090   status=MagickFalse;
1091   (void) CopyMagickString(output_filename,image_info->filename,MaxTextExtent);
1092   (void) CopyMagickString(input_filename,image->filename,MaxTextExtent);
1093   for (i=0; commands[i] != (char *) NULL; i++)
1094   {
1095     status=AcquireUniqueSymbolicLink(output_filename,image_info->filename);
1096     if( IfMagickFalse(AcquireUniqueFilename(image_info->unique)) )
1097       {
1098         ThrowFileException(exception,FileOpenError,
1099           "UnableToCreateTemporaryFile",image_info->unique);
1100         break;
1101       }
1102     if( IfMagickFalse(AcquireUniqueFilename(image_info->zero)) )
1103       {
1104         (void) RelinquishUniqueFileResource(image_info->unique);
1105         ThrowFileException(exception,FileOpenError,
1106           "UnableToCreateTemporaryFile",image_info->zero);
1107         break;
1108       }
1109     if (LocaleCompare(decode,"SCAN") != 0)
1110       {
1111         status=AcquireUniqueSymbolicLink(input_filename,image->filename);
1112         if( IfMagickFalse(status) )
1113           {
1114             ThrowFileException(exception,FileOpenError,
1115               "UnableToCreateTemporaryFile",input_filename);
1116             break;
1117           }
1118       }
1119     status=MagickFalse;
1120     command=InterpretImageProperties(image_info,image,commands[i],exception);
1121     if (command != (char *) NULL)
1122       {
1123         char
1124           *sanitize_command;
1125
1126         /*
1127           Execute delegate.
1128         */
1129         sanitize_command=SanitizeDelegateCommand(command);
1130         status=IsMagickTrue(SystemCommand(delegate_info->spawn,
1131           image_info->verbose,sanitize_command,exception) != 0);
1132         sanitize_command=DestroyString(sanitize_command);
1133         if (IfMagickTrue(delegate_info->spawn))
1134           {
1135             ssize_t
1136               count;
1137
1138             /*
1139               Wait for input file to 'disappear', or maximum 10 seconds.
1140             */
1141             count=100;
1142             while ((count-- > 0) && (access_utf8(image->filename,F_OK) == 0))
1143               (void) MagickDelay(100);  /* sleep 0.1 seconds */
1144           }
1145         command=DestroyString(command);
1146       }
1147     if (LocaleCompare(decode,"SCAN") != 0)
1148       {
1149         if( IfMagickFalse(CopyDelegateFile(image->filename,input_filename)) )
1150           (void) RelinquishUniqueFileResource(input_filename);
1151       }
1152     if( IfMagickFalse(CopyDelegateFile(image_info->filename,output_filename)) )
1153       (void) RelinquishUniqueFileResource(output_filename);
1154     if( IfMagickTrue(image_info->temporary) )
1155       (void) RelinquishUniqueFileResource(image_info->filename);
1156     (void) RelinquishUniqueFileResource(image_info->unique);
1157     (void) RelinquishUniqueFileResource(image_info->zero);
1158     (void) RelinquishUniqueFileResource(image_info->filename);
1159     (void) RelinquishUniqueFileResource(image->filename);
1160     if( IfMagickTrue(status) )
1161       {
1162         (void) ThrowMagickException(exception,GetMagickModule(),DelegateError,
1163           "DelegateFailed","`%s'",commands[i]);
1164         break;
1165       }
1166     commands[i]=DestroyString(commands[i]);
1167   }
1168   (void) CopyMagickString(image_info->filename,output_filename,MaxTextExtent);
1169   (void) CopyMagickString(image->filename,input_filename,MaxTextExtent);
1170   /*
1171     Relinquish resources.
1172   */
1173   for ( ; commands[i] != (char *) NULL; i++)
1174     commands[i]=DestroyString(commands[i]);
1175   commands=(char **) RelinquishMagickMemory(commands);
1176   if( IfMagickTrue(temporary) )
1177     (void) RelinquishUniqueFileResource(image->filename);
1178   return(IsMagickFalse(status));
1179 }
1180 \f
1181 /*
1182 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1183 %                                                                             %
1184 %                                                                             %
1185 %                                                                             %
1186 %  L i s t D e l e g a t e I n f o                                            %
1187 %                                                                             %
1188 %                                                                             %
1189 %                                                                             %
1190 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1191 %
1192 %  ListDelegateInfo() lists the image formats to a file.
1193 %
1194 %  The format of the ListDelegateInfo method is:
1195 %
1196 %      MagickBooleanType ListDelegateInfo(FILE *file,ExceptionInfo *exception)
1197 %
1198 %  A description of each parameter follows.
1199 %
1200 %    o file:  An pointer to a FILE.
1201 %
1202 %    o exception: return any errors or warnings in this structure.
1203 %
1204 */
1205 MagickExport MagickBooleanType ListDelegateInfo(FILE *file,
1206   ExceptionInfo *exception)
1207 {
1208   const DelegateInfo
1209     **delegate_info;
1210
1211   char
1212     **commands,
1213     delegate[MaxTextExtent];
1214
1215   const char
1216     *path;
1217
1218   register ssize_t
1219     i;
1220
1221   size_t
1222     number_delegates;
1223
1224   ssize_t
1225     j;
1226
1227   if (file == (const FILE *) NULL)
1228     file=stdout;
1229   delegate_info=GetDelegateInfoList("*",&number_delegates,exception);
1230   if (delegate_info == (const DelegateInfo **) NULL)
1231     return(MagickFalse);
1232   path=(const char *) NULL;
1233   for (i=0; i < (ssize_t) number_delegates; i++)
1234   {
1235     if( IfMagickTrue(delegate_info[i]->stealth) )
1236       continue;
1237     if ((path == (const char *) NULL) ||
1238         (LocaleCompare(path,delegate_info[i]->path) != 0))
1239       {
1240         if (delegate_info[i]->path != (char *) NULL)
1241           (void) FormatLocaleFile(file,"\nPath: %s\n\n",delegate_info[i]->path);
1242         (void) FormatLocaleFile(file,"Delegate                Command\n");
1243         (void) FormatLocaleFile(file,
1244           "-------------------------------------------------"
1245           "------------------------------\n");
1246       }
1247     path=delegate_info[i]->path;
1248     *delegate='\0';
1249     if (delegate_info[i]->encode != (char *) NULL)
1250       (void) CopyMagickString(delegate,delegate_info[i]->encode,MaxTextExtent);
1251     (void) ConcatenateMagickString(delegate,"        ",MaxTextExtent);
1252     delegate[8]='\0';
1253     commands=StringToList(delegate_info[i]->commands);
1254     if (commands == (char **) NULL)
1255       continue;
1256     (void) FormatLocaleFile(file,"%11s%c=%c%s  ",delegate_info[i]->decode ?
1257       delegate_info[i]->decode : "",delegate_info[i]->mode <= 0 ? '<' : ' ',
1258       delegate_info[i]->mode >= 0 ? '>' : ' ',delegate);
1259     StripString(commands[0]);
1260     (void) FormatLocaleFile(file,"\"%s\"\n",commands[0]);
1261     for (j=1; commands[j] != (char *) NULL; j++)
1262     {
1263       StripString(commands[j]);
1264       (void) FormatLocaleFile(file,"                     \"%s\"\n",commands[j]);
1265     }
1266     for (j=0; commands[j] != (char *) NULL; j++)
1267       commands[j]=DestroyString(commands[j]);
1268     commands=(char **) RelinquishMagickMemory(commands);
1269   }
1270   (void) fflush(file);
1271   delegate_info=(const DelegateInfo **)
1272     RelinquishMagickMemory((void *) delegate_info);
1273   return(MagickTrue);
1274 }
1275 \f
1276 /*
1277 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1278 %                                                                             %
1279 %                                                                             %
1280 %                                                                             %
1281 +   L o a d D e l e g a t e L i s t                                           %
1282 %                                                                             %
1283 %                                                                             %
1284 %                                                                             %
1285 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1286 %
1287 %  LoadDelegateCache() loads the delegate configurations which provides a
1288 %  mapping between delegate attributes and a delegate name.
1289 %
1290 %  The format of the LoadDelegateCache method is:
1291 %
1292 %      MagickBooleanType LoadDelegateCache(LinkedListInfo *delegate_cache,
1293 %        const char *xml,const char *filename,const size_t depth,
1294 %        ExceptionInfo *exception)
1295 %
1296 %  A description of each parameter follows:
1297 %
1298 %    o xml:  The delegate list in XML format.
1299 %
1300 %    o filename:  The delegate list filename.
1301 %
1302 %    o depth: depth of <include /> statements.
1303 %
1304 %    o exception: return any errors or warnings in this structure.
1305 %
1306 */
1307 static MagickBooleanType LoadDelegateCache(LinkedListInfo *delegate_cache,
1308   const char *xml,const char *filename,const size_t depth,
1309   ExceptionInfo *exception)
1310 {
1311   char
1312     keyword[MaxTextExtent],
1313     *token;
1314
1315   const char
1316     *q;
1317
1318   DelegateInfo
1319     *delegate_info;
1320
1321   MagickBooleanType
1322     status;
1323
1324   /*
1325     Load the delegate map file.
1326   */
1327   (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1328     "Loading delegate configuration file \"%s\" ...",filename);
1329   if (xml == (const char *) NULL)
1330     return(MagickFalse);
1331   status=MagickTrue;
1332   delegate_info=(DelegateInfo *) NULL;
1333   token=AcquireString(xml);
1334   for (q=(const char *) xml; *q != '\0'; )
1335   {
1336     /*
1337       Interpret XML.
1338     */
1339     GetMagickToken(q,&q,token);
1340     if (*token == '\0')
1341       break;
1342     (void) CopyMagickString(keyword,token,MaxTextExtent);
1343     if (LocaleNCompare(keyword,"<!DOCTYPE",9) == 0)
1344       {
1345         /*
1346           Doctype element.
1347         */
1348         while ((LocaleNCompare(q,"]>",2) != 0) && (*q != '\0'))
1349           GetMagickToken(q,&q,token);
1350         continue;
1351       }
1352     if (LocaleNCompare(keyword,"<!--",4) == 0)
1353       {
1354         /*
1355           Comment element.
1356         */
1357         while ((LocaleNCompare(q,"->",2) != 0) && (*q != '\0'))
1358           GetMagickToken(q,&q,token);
1359         continue;
1360       }
1361     if (LocaleCompare(keyword,"<include") == 0)
1362       {
1363         /*
1364           Include element.
1365         */
1366         while (((*token != '/') && (*(token+1) != '>')) && (*q != '\0'))
1367         {
1368           (void) CopyMagickString(keyword,token,MaxTextExtent);
1369           GetMagickToken(q,&q,token);
1370           if (*token != '=')
1371             continue;
1372           GetMagickToken(q,&q,token);
1373           if (LocaleCompare(keyword,"file") == 0)
1374             {
1375               if (depth > 200)
1376                 (void) ThrowMagickException(exception,GetMagickModule(),
1377                   ConfigureError,"IncludeElementNestedTooDeeply","`%s'",token);
1378               else
1379                 {
1380                   char
1381                     path[MaxTextExtent],
1382                     *xml;
1383
1384                   GetPathComponent(filename,HeadPath,path);
1385                   if (*path != '\0')
1386                     (void) ConcatenateMagickString(path,DirectorySeparator,
1387                       MaxTextExtent);
1388                   if (*token == *DirectorySeparator)
1389                     (void) CopyMagickString(path,token,MaxTextExtent);
1390                   else
1391                     (void) ConcatenateMagickString(path,token,MaxTextExtent);
1392                   xml=FileToXML(path,~0UL);
1393                   if (xml != (char *) NULL)
1394                     {
1395                       status&=LoadDelegateCache(delegate_cache,xml,path,
1396                         depth+1,exception);
1397                       xml=(char *) RelinquishMagickMemory(xml);
1398                     }
1399                 }
1400             }
1401         }
1402         continue;
1403       }
1404     if (LocaleCompare(keyword,"<delegate") == 0)
1405       {
1406         /*
1407           Delegate element.
1408         */
1409         delegate_info=(DelegateInfo *) AcquireMagickMemory(
1410           sizeof(*delegate_info));
1411         if (delegate_info == (DelegateInfo *) NULL)
1412           ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1413         (void) ResetMagickMemory(delegate_info,0,sizeof(*delegate_info));
1414         delegate_info->path=ConstantString(filename);
1415         delegate_info->thread_support=MagickTrue;
1416         delegate_info->signature=MagickSignature;
1417         continue;
1418       }
1419     if (delegate_info == (DelegateInfo *) NULL)
1420       continue;
1421     if (LocaleCompare(keyword,"/>") == 0)
1422       {
1423         status=AppendValueToLinkedList(delegate_cache,delegate_info);
1424         if( IfMagickFalse(status) )
1425           (void) ThrowMagickException(exception,GetMagickModule(),
1426             ResourceLimitError,"MemoryAllocationFailed","`%s'",
1427             delegate_info->commands);
1428         delegate_info=(DelegateInfo *) NULL;
1429         continue;
1430       }
1431     GetMagickToken(q,(const char **) NULL,token);
1432     if (*token != '=')
1433       continue;
1434     GetMagickToken(q,&q,token);
1435     GetMagickToken(q,&q,token);
1436     switch (*keyword)
1437     {
1438       case 'C':
1439       case 'c':
1440       {
1441         if (LocaleCompare((char *) keyword,"command") == 0)
1442           {
1443             char
1444               *commands;
1445
1446             commands=AcquireString(token);
1447 #if defined(MAGICKCORE_WINDOWS_SUPPORT)
1448             if (strchr(commands,'@') != (char *) NULL)
1449               {
1450                 char
1451                   path[MaxTextExtent];
1452
1453                 NTGhostscriptEXE(path,MaxTextExtent);
1454                 (void) SubstituteString((char **) &commands,"@PSDelegate@",
1455                   path);
1456                 (void) SubstituteString((char **) &commands,"\\","/");
1457               }
1458 #endif
1459             (void) SubstituteString((char **) &commands,"&amp;","&");
1460             (void) SubstituteString((char **) &commands,"&quot;","\"");
1461             (void) SubstituteString((char **) &commands,"&gt;",">");
1462             (void) SubstituteString((char **) &commands,"&lt;","<");
1463             delegate_info->commands=commands;
1464             break;
1465           }
1466         break;
1467       }
1468       case 'D':
1469       case 'd':
1470       {
1471         if (LocaleCompare((char *) keyword,"decode") == 0)
1472           {
1473             delegate_info->decode=ConstantString(token);
1474             delegate_info->mode=1;
1475             break;
1476           }
1477         break;
1478       }
1479       case 'E':
1480       case 'e':
1481       {
1482         if (LocaleCompare((char *) keyword,"encode") == 0)
1483           {
1484             delegate_info->encode=ConstantString(token);
1485             delegate_info->mode=(-1);
1486             break;
1487           }
1488         break;
1489       }
1490       case 'M':
1491       case 'm':
1492       {
1493         if (LocaleCompare((char *) keyword,"mode") == 0)
1494           {
1495             delegate_info->mode=1;
1496             if (LocaleCompare(token,"bi") == 0)
1497               delegate_info->mode=0;
1498             else
1499               if (LocaleCompare(token,"encode") == 0)
1500                 delegate_info->mode=(-1);
1501             break;
1502           }
1503         break;
1504       }
1505       case 'S':
1506       case 's':
1507       {
1508         if (LocaleCompare((char *) keyword,"spawn") == 0)
1509           {
1510             delegate_info->spawn=IsStringTrue(token);
1511             break;
1512           }
1513         if (LocaleCompare((char *) keyword,"stealth") == 0)
1514           {
1515             delegate_info->stealth=IsStringTrue(token);
1516             break;
1517           }
1518         break;
1519       }
1520       case 'T':
1521       case 't':
1522       {
1523         if (LocaleCompare((char *) keyword,"thread-support") == 0)
1524           {
1525             delegate_info->thread_support=IsStringTrue(token);
1526             if (delegate_info->thread_support == MagickFalse)
1527               delegate_info->semaphore=AcquireSemaphoreInfo();
1528             break;
1529           }
1530         break;
1531       }
1532       default:
1533         break;
1534     }
1535   }
1536   token=(char *) RelinquishMagickMemory(token);
1537   return(status);
1538 }