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