]> granicus.if.org Git - imagemagick/blob - MagickCore/delegate.c
Fixed command line execution in Windows for delegates that move files
[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   {
450     register char
451       *p;
452
453     /*
454       If a command shell is executed we need to change the forward slashes in
455       files to a backslash. We need to do this to keep Windows happy when we
456       want to 'move' a file.
457
458       TODO: This won't work if one of the delegate parameters has a forward
459             slash as aparameter.
460     */
461     p=strstr(sanitize_command, "cmd.exe /c");
462     if (p != (char*) NULL)
463       {
464         p+=10;
465         for (; *p != '\0'; p++)
466           if (*p == '/')
467             *p=*DirectorySeparator;
468       }
469   }
470   status=NTSystemCommand(sanitize_command,message);
471 #elif defined(macintosh)
472   status=MACSystemCommand(sanitize_command);
473 #elif defined(vms)
474   status=system(sanitize_command);
475 #else
476 #  error No suitable system() method.
477 #endif
478   if (status < 0)
479     {
480       if ((message != (char *) NULL) && (*message != '\0'))
481         (void) ThrowMagickException(exception,GetMagickModule(),DelegateError,
482           "FailedToExecuteCommand","`%s' (%s)",sanitize_command,message);
483       else
484         (void) ThrowMagickException(exception,GetMagickModule(),DelegateError,
485           "FailedToExecuteCommand","`%s' (%d)",sanitize_command,status);
486     }
487   sanitize_command=DestroyString(sanitize_command);
488   for (i=0; i < (ssize_t) number_arguments; i++)
489     arguments[i]=DestroyString(arguments[i]);
490   arguments=(char **) RelinquishMagickMemory(arguments);
491   return(status);
492 }
493 \f
494 /*
495 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
496 %                                                                             %
497 %                                                                             %
498 %                                                                             %
499 %   G e t D e l e g a t e C o m m a n d                                       %
500 %                                                                             %
501 %                                                                             %
502 %                                                                             %
503 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
504 %
505 %  GetDelegateCommand() replaces any embedded formatting characters with the
506 %  appropriate image attribute and returns the resulting command.
507 %
508 %  The format of the GetDelegateCommand method is:
509 %
510 %      char *GetDelegateCommand(const ImageInfo *image_info,Image *image,
511 %        const char *decode,const char *encode,ExceptionInfo *exception)
512 %
513 %  A description of each parameter follows:
514 %
515 %    o command: Method GetDelegateCommand returns the command associated
516 %      with specified delegate tag.
517 %
518 %    o image_info: the image info.
519 %
520 %    o image: the image.
521 %
522 %    o decode: Specifies the decode delegate we are searching for as a
523 %      character string.
524 %
525 %    o encode: Specifies the encode delegate we are searching for as a
526 %      character string.
527 %
528 %    o exception: return any errors or warnings in this structure.
529 %
530 */
531 MagickExport char *GetDelegateCommand(const ImageInfo *image_info,Image *image,
532   const char *decode,const char *encode,ExceptionInfo *exception)
533 {
534   char
535     *command,
536     **commands;
537
538   const DelegateInfo
539     *delegate_info;
540
541   register ssize_t
542     i;
543
544   assert(image_info != (ImageInfo *) NULL);
545   assert(image_info->signature == MagickSignature);
546   assert(image != (Image *) NULL);
547   assert(image->signature == MagickSignature);
548   if (image->debug != MagickFalse)
549     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
550
551   delegate_info=GetDelegateInfo(decode,encode,exception);
552   if (delegate_info == (const DelegateInfo *) NULL)
553     {
554       (void) ThrowMagickException(exception,GetMagickModule(),DelegateError,
555         "NoTagFound","`%s'",decode ? decode : encode);
556       return((char *) NULL);
557     }
558   commands=StringToList(delegate_info->commands);
559   if (commands == (char **) NULL)
560     {
561       (void) ThrowMagickException(exception,GetMagickModule(),
562         ResourceLimitError,"MemoryAllocationFailed","`%s'",decode ? decode :
563         encode);
564       return((char *) NULL);
565     }
566   command=InterpretImageProperties((ImageInfo *) image_info,image,commands[0],
567     exception);
568   if (command == (char *) NULL)
569     (void) ThrowMagickException(exception,GetMagickModule(),ResourceLimitError,
570       "MemoryAllocationFailed","`%s'",commands[0]);
571   /*
572     Relinquish resources.
573   */
574   for (i=0; commands[i] != (char *) NULL; i++)
575     commands[i]=DestroyString(commands[i]);
576   commands=(char **) RelinquishMagickMemory(commands);
577   return(command);
578 }
579 \f
580 /*
581 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
582 %                                                                             %
583 %                                                                             %
584 %                                                                             %
585 %   G e t D e l e g a t e C o m m a n d s                                     %
586 %                                                                             %
587 %                                                                             %
588 %                                                                             %
589 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
590 %
591 %  GetDelegateCommands() returns the commands associated with a delegate.
592 %
593 %  The format of the GetDelegateCommands method is:
594 %
595 %      const char *GetDelegateCommands(const DelegateInfo *delegate_info)
596 %
597 %  A description of each parameter follows:
598 %
599 %    o delegate_info:  The delegate info.
600 %
601 */
602 MagickExport const char *GetDelegateCommands(const DelegateInfo *delegate_info)
603 {
604   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
605
606   assert(delegate_info != (DelegateInfo *) NULL);
607   assert(delegate_info->signature == MagickSignature);
608   return(delegate_info->commands);
609 }
610 \f
611 /*
612 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
613 %                                                                             %
614 %                                                                             %
615 %                                                                             %
616 %   G e t D e l e g a t e I n f o                                             %
617 %                                                                             %
618 %                                                                             %
619 %                                                                             %
620 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
621 %
622 %  GetDelegateInfo() returns any delegates associated with the specified tag.
623 %
624 %  The format of the GetDelegateInfo method is:
625 %
626 %      const DelegateInfo *GetDelegateInfo(const char *decode,
627 %        const char *encode,ExceptionInfo *exception)
628 %
629 %  A description of each parameter follows:
630 %
631 %    o decode: Specifies the decode delegate we are searching for as a
632 %      character string.
633 %
634 %    o encode: Specifies the encode delegate we are searching for as a
635 %      character string.
636 %
637 %    o exception: return any errors or warnings in this structure.
638 %
639 */
640 MagickExport const DelegateInfo *GetDelegateInfo(const char *decode,
641   const char *encode,ExceptionInfo *exception)
642 {
643   register const DelegateInfo
644     *p;
645
646   assert(exception != (ExceptionInfo *) NULL);
647   if (IfMagickFalse(IsDelegateCacheInstantiated(exception)))
648     return((const DelegateInfo *) NULL);
649   /*
650     Search for named delegate.
651   */
652   LockSemaphoreInfo(delegate_semaphore);
653   ResetLinkedListIterator(delegate_cache);
654   p=(const DelegateInfo *) GetNextValueInLinkedList(delegate_cache);
655   if ((LocaleCompare(decode,"*") == 0) && (LocaleCompare(encode,"*") == 0))
656     {
657       UnlockSemaphoreInfo(delegate_semaphore);
658       return(p);
659     }
660   while (p != (const DelegateInfo *) NULL)
661   {
662     if (p->mode > 0)
663       {
664         if (LocaleCompare(p->decode,decode) == 0)
665           break;
666         p=(const DelegateInfo *) GetNextValueInLinkedList(delegate_cache);
667         continue;
668       }
669     if (p->mode < 0)
670       {
671         if (LocaleCompare(p->encode,encode) == 0)
672           break;
673         p=(const DelegateInfo *) GetNextValueInLinkedList(delegate_cache);
674         continue;
675       }
676     if (LocaleCompare(decode,p->decode) == 0)
677       if (LocaleCompare(encode,p->encode) == 0)
678         break;
679     if (LocaleCompare(decode,"*") == 0)
680       if (LocaleCompare(encode,p->encode) == 0)
681         break;
682     if (LocaleCompare(decode,p->decode) == 0)
683       if (LocaleCompare(encode,"*") == 0)
684         break;
685     p=(const DelegateInfo *) GetNextValueInLinkedList(delegate_cache);
686   }
687   if (p != (const DelegateInfo *) NULL)
688     (void) InsertValueInLinkedList(delegate_cache,0,
689       RemoveElementByValueFromLinkedList(delegate_cache,p));
690   UnlockSemaphoreInfo(delegate_semaphore);
691   return(p);
692 }
693 \f
694 /*
695 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
696 %                                                                             %
697 %                                                                             %
698 %                                                                             %
699 %   G e t D e l e g a t e I n f o L i s t                                     %
700 %                                                                             %
701 %                                                                             %
702 %                                                                             %
703 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
704 %
705 %  GetDelegateInfoList() returns any delegates that match the specified pattern.
706 %
707 %  The delegate of the GetDelegateInfoList function is:
708 %
709 %      const DelegateInfo **GetDelegateInfoList(const char *pattern,
710 %        size_t *number_delegates,ExceptionInfo *exception)
711 %
712 %  A description of each parameter follows:
713 %
714 %    o pattern: Specifies a pointer to a text string containing a pattern.
715 %
716 %    o number_delegates:  This integer returns the number of delegates in the
717 %      list.
718 %
719 %    o exception: return any errors or warnings in this structure.
720 %
721 */
722
723 #if defined(__cplusplus) || defined(c_plusplus)
724 extern "C" {
725 #endif
726
727 static int DelegateInfoCompare(const void *x,const void *y)
728 {
729   const DelegateInfo
730     **p,
731     **q;
732
733   int
734     cmp;
735
736   p=(const DelegateInfo **) x,
737   q=(const DelegateInfo **) y;
738   cmp=LocaleCompare((*p)->path,(*q)->path);
739   if (cmp == 0)
740     {
741       if ((*p)->decode == (char *) NULL)
742         if (((*p)->encode != (char *) NULL) &&
743             ((*q)->encode != (char *) NULL))
744           return(strcmp((*p)->encode,(*q)->encode));
745       if (((*p)->decode != (char *) NULL) &&
746           ((*q)->decode != (char *) NULL))
747         return(strcmp((*p)->decode,(*q)->decode));
748     }
749   return(cmp);
750 }
751
752 #if defined(__cplusplus) || defined(c_plusplus)
753 }
754 #endif
755
756 MagickExport const DelegateInfo **GetDelegateInfoList(const char *pattern,
757   size_t *number_delegates,ExceptionInfo *exception)
758 {
759   const DelegateInfo
760     **delegates;
761
762   register const DelegateInfo
763     *p;
764
765   register ssize_t
766     i;
767
768   /*
769     Allocate delegate list.
770   */
771   assert(number_delegates != (size_t *) NULL);
772   assert(pattern != (char *) NULL);
773   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",pattern);
774
775   *number_delegates=0;
776   p=GetDelegateInfo("*","*",exception);
777   if (p == (const DelegateInfo *) NULL)
778     return((const DelegateInfo **) NULL);
779   delegates=(const DelegateInfo **) AcquireQuantumMemory((size_t)
780     GetNumberOfElementsInLinkedList(delegate_cache)+1UL,sizeof(*delegates));
781   if (delegates == (const DelegateInfo **) NULL)
782     return((const DelegateInfo **) NULL);
783   /*
784     Generate delegate list.
785   */
786   LockSemaphoreInfo(delegate_semaphore);
787   ResetLinkedListIterator(delegate_cache);
788   p=(const DelegateInfo *) GetNextValueInLinkedList(delegate_cache);
789   for (i=0; p != (const DelegateInfo *) NULL; )
790   {
791     if( IfMagickFalse(p->stealth) &&
792         ( IfMagickTrue(GlobExpression(p->decode,pattern,MagickFalse)) ||
793           IfMagickTrue(GlobExpression(p->encode,pattern,MagickFalse))) )
794       delegates[i++]=p;
795     p=(const DelegateInfo *) GetNextValueInLinkedList(delegate_cache);
796   }
797   UnlockSemaphoreInfo(delegate_semaphore);
798   qsort((void *) delegates,(size_t) i,sizeof(*delegates),DelegateInfoCompare);
799   delegates[i]=(DelegateInfo *) NULL;
800   *number_delegates=(size_t) i;
801   return(delegates);
802 }
803 \f
804 /*
805 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
806 %                                                                             %
807 %                                                                             %
808 %                                                                             %
809 %   G e t D e l e g a t e L i s t                                             %
810 %                                                                             %
811 %                                                                             %
812 %                                                                             %
813 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
814 %
815 %  GetDelegateList() returns any image format delegates that match the
816 %  specified  pattern.
817 %
818 %  The format of the GetDelegateList function is:
819 %
820 %      char **GetDelegateList(const char *pattern,
821 %        size_t *number_delegates,ExceptionInfo *exception)
822 %
823 %  A description of each parameter follows:
824 %
825 %    o pattern: Specifies a pointer to a text string containing a pattern.
826 %
827 %    o number_delegates:  This integer returns the number of delegates
828 %      in the list.
829 %
830 %    o exception: return any errors or warnings in this structure.
831 %
832 */
833
834 #if defined(__cplusplus) || defined(c_plusplus)
835 extern "C" {
836 #endif
837
838 static int DelegateCompare(const void *x,const void *y)
839 {
840   register const char
841     **p,
842     **q;
843
844   p=(const char **) x;
845   q=(const char **) y;
846   return(LocaleCompare(*p,*q));
847 }
848
849 #if defined(__cplusplus) || defined(c_plusplus)
850 }
851 #endif
852
853 MagickExport char **GetDelegateList(const char *pattern,
854   size_t *number_delegates,ExceptionInfo *exception)
855 {
856   char
857     **delegates;
858
859   register const DelegateInfo
860     *p;
861
862   register ssize_t
863     i;
864
865   /*
866     Allocate delegate list.
867   */
868   assert(pattern != (char *) NULL);
869   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",pattern);
870
871   assert(number_delegates != (size_t *) NULL);
872   *number_delegates=0;
873   p=GetDelegateInfo("*","*",exception);
874   if (p == (const DelegateInfo *) NULL)
875     return((char **) NULL);
876   delegates=(char **) AcquireQuantumMemory((size_t)
877     GetNumberOfElementsInLinkedList(delegate_cache)+1UL,sizeof(*delegates));
878   if (delegates == (char **) NULL)
879     return((char **) NULL);
880   LockSemaphoreInfo(delegate_semaphore);
881   ResetLinkedListIterator(delegate_cache);
882   p=(const DelegateInfo *) GetNextValueInLinkedList(delegate_cache);
883   for (i=0; p != (const DelegateInfo *) NULL; )
884   {
885     if( IfMagickFalse(p->stealth) &&
886         IfMagickTrue(GlobExpression(p->decode,pattern,MagickFalse)) )
887       delegates[i++]=ConstantString(p->decode);
888     if( IfMagickFalse(p->stealth) &&
889         IfMagickTrue(GlobExpression(p->encode,pattern,MagickFalse)) )
890       delegates[i++]=ConstantString(p->encode);
891     p=(const DelegateInfo *) GetNextValueInLinkedList(delegate_cache);
892   }
893   UnlockSemaphoreInfo(delegate_semaphore);
894   qsort((void *) delegates,(size_t) i,sizeof(*delegates),DelegateCompare);
895   delegates[i]=(char *) NULL;
896   *number_delegates=(size_t) i;
897   return(delegates);
898 }
899 \f
900 /*
901 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
902 %                                                                             %
903 %                                                                             %
904 %                                                                             %
905 %   G e t D e l e g a t e M o d e                                             %
906 %                                                                             %
907 %                                                                             %
908 %                                                                             %
909 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
910 %
911 %  GetDelegateMode() returns the mode of the delegate.
912 %
913 %  The format of the GetDelegateMode method is:
914 %
915 %      ssize_t GetDelegateMode(const DelegateInfo *delegate_info)
916 %
917 %  A description of each parameter follows:
918 %
919 %    o delegate_info:  The delegate info.
920 %
921 */
922 MagickExport ssize_t GetDelegateMode(const DelegateInfo *delegate_info)
923 {
924   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
925
926   assert(delegate_info != (DelegateInfo *) NULL);
927   assert(delegate_info->signature == MagickSignature);
928   return(delegate_info->mode);
929 }
930 \f
931 /*
932 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
933 %                                                                             %
934 %                                                                             %
935 %                                                                             %
936 +   G e t D e l e g a t e T h r e a d S u p p o r t                           %
937 %                                                                             %
938 %                                                                             %
939 %                                                                             %
940 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
941 %
942 %  GetDelegateThreadSupport() returns MagickTrue if the delegate supports
943 %  threads.
944 %
945 %  The format of the GetDelegateThreadSupport method is:
946 %
947 %      MagickBooleanType GetDelegateThreadSupport(
948 %        const DelegateInfo *delegate_info)
949 %
950 %  A description of each parameter follows:
951 %
952 %    o delegate_info:  The delegate info.
953 %
954 */
955 MagickExport MagickBooleanType GetDelegateThreadSupport(
956   const DelegateInfo *delegate_info)
957 {
958   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
959
960   assert(delegate_info != (DelegateInfo *) NULL);
961   assert(delegate_info->signature == MagickSignature);
962   return(delegate_info->thread_support);
963 }
964 \f
965 /*
966 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
967 %                                                                             %
968 %                                                                             %
969 %                                                                             %
970 +   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                     %
971 %                                                                             %
972 %                                                                             %
973 %                                                                             %
974 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
975 %
976 %  IsDelegateCacheInstantiated() determines if the delegate cache is
977 %  instantiated.  If not, it instantiates the cache and returns it.
978 %
979 %  The format of the IsDelegateInstantiated method is:
980 %
981 %      MagickBooleanType IsDelegateCacheInstantiated(ExceptionInfo *exception)
982 %
983 %  A description of each parameter follows.
984 %
985 %    o exception: return any errors or warnings in this structure.
986 %
987 */
988 static MagickBooleanType IsDelegateCacheInstantiated(ExceptionInfo *exception)
989 {
990   if (delegate_cache == (LinkedListInfo *) NULL)
991     {
992       if (delegate_semaphore == (SemaphoreInfo *) NULL)
993         ActivateSemaphoreInfo(&delegate_semaphore);
994       LockSemaphoreInfo(delegate_semaphore);
995       if (delegate_cache == (LinkedListInfo *) NULL)
996         delegate_cache=AcquireDelegateCache(DelegateFilename,exception);
997       UnlockSemaphoreInfo(delegate_semaphore);
998     }
999   return(IsMagickNotNULL(delegate_cache));
1000 }
1001 \f
1002 /*
1003 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1004 %                                                                             %
1005 %                                                                             %
1006 %                                                                             %
1007 %   I n v o k e D e l e g a t e                                               %
1008 %                                                                             %
1009 %                                                                             %
1010 %                                                                             %
1011 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1012 %
1013 %  InvokeDelegate replaces any embedded formatting characters with the
1014 %  appropriate image attribute and executes the resulting command.  MagickFalse
1015 %  is returned if the commands execute with success otherwise MagickTrue.
1016 %
1017 %  The format of the InvokeDelegate method is:
1018 %
1019 %      MagickBooleanType InvokeDelegate(ImageInfo *image_info,Image *image,
1020 %        const char *decode,const char *encode,ExceptionInfo *exception)
1021 %
1022 %  A description of each parameter follows:
1023 %
1024 %    o image_info: the imageInfo.
1025 %
1026 %    o image: the image.
1027 %
1028 %    o exception: return any errors or warnings in this structure.
1029 %
1030 */
1031
1032 static MagickBooleanType CopyDelegateFile(const char *source,
1033   const char *destination)
1034 {
1035   int
1036     destination_file,
1037     source_file;
1038
1039   MagickBooleanType
1040     status;
1041
1042   register size_t
1043     i;
1044
1045   size_t
1046     length,
1047     quantum;
1048
1049   ssize_t
1050     count;
1051
1052   struct stat
1053     attributes;
1054
1055   unsigned char
1056     *buffer;
1057
1058   /*
1059     Copy source file to destination.
1060   */
1061   assert(source != (const char *) NULL);
1062   assert(destination != (char *) NULL);
1063   status=GetPathAttributes(destination,&attributes);
1064   if( IfMagickTrue(status) && (attributes.st_size != 0))
1065     return(MagickTrue);
1066   destination_file=open_utf8(destination,O_WRONLY | O_BINARY | O_CREAT,S_MODE);
1067   if (destination_file == -1)
1068     return(MagickFalse);
1069   source_file=open_utf8(source,O_RDONLY | O_BINARY,0);
1070   if (source_file == -1)
1071     {
1072       (void) close(destination_file);
1073       return(MagickFalse);
1074     }
1075   quantum=(size_t) MagickMaxBufferExtent;
1076   if ((fstat(source_file,&attributes) == 0) && (attributes.st_size != 0))
1077     quantum=MagickMin((size_t) attributes.st_size,MagickMaxBufferExtent);
1078   buffer=(unsigned char *) AcquireQuantumMemory(quantum,sizeof(*buffer));
1079   if (buffer == (unsigned char *) NULL)
1080     {
1081       (void) close(source_file);
1082       (void) close(destination_file);
1083       return(MagickFalse);
1084     }
1085   length=0;
1086   for (i=0; ; i+=count)
1087   {
1088     count=(ssize_t) read(source_file,buffer,quantum);
1089     if (count <= 0)
1090       break;
1091     length=(size_t) count;
1092     count=(ssize_t) write(destination_file,buffer,length);
1093     if ((size_t) count != length)
1094       break;
1095   }
1096   (void) close(destination_file);
1097   (void) close(source_file);
1098   buffer=(unsigned char *) RelinquishMagickMemory(buffer);
1099   return(IsMagickTrue(i!=0));
1100 }
1101
1102 MagickExport MagickBooleanType InvokeDelegate(ImageInfo *image_info,
1103   Image *image,const char *decode,const char *encode,ExceptionInfo *exception)
1104 {
1105   char
1106     *command,
1107     **commands,
1108     input_filename[MaxTextExtent],
1109     output_filename[MaxTextExtent];
1110
1111   const DelegateInfo
1112     *delegate_info;
1113
1114   MagickBooleanType
1115     status,
1116     temporary;
1117
1118   register ssize_t
1119     i;
1120
1121   PolicyRights
1122     rights;
1123
1124   /*
1125     Get delegate.
1126   */
1127   assert(image_info != (ImageInfo *) NULL);
1128   assert(image_info->signature == MagickSignature);
1129   assert(image != (Image *) NULL);
1130   assert(image->signature == MagickSignature);
1131   if (image->debug != MagickFalse)
1132     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1133
1134   rights=ExecutePolicyRights;
1135   if( IfMagickFalse(IsRightsAuthorized(DelegatePolicyDomain,rights,decode)) )
1136     {
1137       errno=EPERM;
1138       (void) ThrowMagickException(exception,GetMagickModule(),PolicyError,
1139         "NotAuthorized","`%s'",decode);
1140       return(MagickFalse);
1141     }
1142   if( IfMagickFalse(IsRightsAuthorized(DelegatePolicyDomain,rights,encode)) )
1143     {
1144       errno=EPERM;
1145       (void) ThrowMagickException(exception,GetMagickModule(),PolicyError,
1146         "NotAuthorized","`%s'",encode);
1147       return(MagickFalse);
1148     }
1149   temporary=IsMagickTrue(*image->filename == '\0');
1150   if( IfMagickTrue(temporary) )
1151     if( IfMagickFalse(AcquireUniqueFilename(image->filename)) )
1152       {
1153         ThrowFileException(exception,FileOpenError,
1154           "UnableToCreateTemporaryFile",image->filename);
1155         return(MagickFalse);
1156       }
1157   delegate_info=GetDelegateInfo(decode,encode,exception);
1158   if (delegate_info == (DelegateInfo *) NULL)
1159     {
1160       if( IfMagickTrue(temporary) )
1161         (void) RelinquishUniqueFileResource(image->filename);
1162       (void) ThrowMagickException(exception,GetMagickModule(),DelegateError,
1163         "NoTagFound","`%s'",decode ? decode : encode);
1164       return(MagickFalse);
1165     }
1166   if (*image_info->filename == '\0')
1167     {
1168       if( IfMagickFalse(AcquireUniqueFilename(image_info->filename)) )
1169         {
1170           if( IfMagickTrue(temporary) )
1171             (void) RelinquishUniqueFileResource(image->filename);
1172           ThrowFileException(exception,FileOpenError,
1173             "UnableToCreateTemporaryFile",image_info->filename);
1174           return(MagickFalse);
1175         }
1176       image_info->temporary=MagickTrue;
1177     }
1178   if ((delegate_info->mode != 0) && (((decode != (const char *) NULL) &&
1179         (delegate_info->encode != (char *) NULL)) ||
1180        ((encode != (const char *) NULL) &&
1181         (delegate_info->decode != (char *) NULL))))
1182     {
1183       char
1184         *magick;
1185
1186       ImageInfo
1187         *clone_info;
1188
1189       register Image
1190         *p;
1191
1192       /*
1193         Delegate requires a particular image format.
1194       */
1195       if( IfMagickFalse(AcquireUniqueFilename(image_info->unique)) )
1196         {
1197           ThrowFileException(exception,FileOpenError,
1198             "UnableToCreateTemporaryFile",image_info->unique);
1199           return(MagickFalse);
1200         }
1201       if( IfMagickFalse(AcquireUniqueFilename(image_info->zero)) )
1202         {
1203           (void) RelinquishUniqueFileResource(image_info->unique);
1204           ThrowFileException(exception,FileOpenError,
1205             "UnableToCreateTemporaryFile",image_info->zero);
1206           return(MagickFalse);
1207         }
1208       magick=InterpretImageProperties(image_info,image,decode != (char *) NULL ?
1209         delegate_info->encode : delegate_info->decode,exception);
1210       if (magick == (char *) NULL)
1211         {
1212           (void) RelinquishUniqueFileResource(image_info->unique);
1213           (void) RelinquishUniqueFileResource(image_info->zero);
1214           if( IfMagickTrue(temporary) )
1215             (void) RelinquishUniqueFileResource(image->filename);
1216           (void) ThrowMagickException(exception,GetMagickModule(),
1217             DelegateError,"DelegateFailed","`%s'",decode ? decode : encode);
1218           return(MagickFalse);
1219         }
1220       LocaleUpper(magick);
1221       clone_info=CloneImageInfo(image_info);
1222       (void) CopyMagickString((char *) clone_info->magick,magick,
1223         MaxTextExtent);
1224       if (LocaleCompare(magick,"NULL") != 0)
1225         (void) CopyMagickString(image->magick,magick,MaxTextExtent);
1226       magick=DestroyString(magick);
1227       (void) FormatLocaleString(clone_info->filename,MaxTextExtent,"%s:",
1228         delegate_info->decode);
1229       (void) SetImageInfo(clone_info,(unsigned int) GetImageListLength(image),
1230         exception);
1231       (void) CopyMagickString(clone_info->filename,image_info->filename,
1232         MaxTextExtent);
1233       (void) CopyMagickString(image_info->filename,image->filename,
1234         MaxTextExtent);
1235       for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
1236       {
1237         (void) FormatLocaleString(p->filename,MaxTextExtent,"%s:%s",
1238           delegate_info->decode,clone_info->filename);
1239         status=WriteImage(clone_info,p,exception);
1240         if( IfMagickFalse(status) )
1241           {
1242             (void) RelinquishUniqueFileResource(image_info->unique);
1243             (void) RelinquishUniqueFileResource(image_info->zero);
1244             if( IfMagickTrue(temporary) )
1245               (void) RelinquishUniqueFileResource(image->filename);
1246             clone_info=DestroyImageInfo(clone_info);
1247             (void) ThrowMagickException(exception,GetMagickModule(),
1248               DelegateError,"DelegateFailed","`%s'",decode ? decode : encode);
1249             return(MagickFalse);
1250           }
1251         if( IfMagickTrue(clone_info->adjoin) )
1252           break;
1253       }
1254       (void) RelinquishUniqueFileResource(image_info->unique);
1255       (void) RelinquishUniqueFileResource(image_info->zero);
1256       clone_info=DestroyImageInfo(clone_info);
1257     }
1258   /*
1259     Invoke delegate.
1260   */
1261   commands=StringToList(delegate_info->commands);
1262   if (commands == (char **) NULL)
1263     {
1264       if( IfMagickTrue(temporary) )
1265         (void) RelinquishUniqueFileResource(image->filename);
1266       (void) ThrowMagickException(exception,GetMagickModule(),
1267         ResourceLimitError,"MemoryAllocationFailed","`%s'",
1268         decode ? decode : encode);
1269       return(MagickFalse);
1270     }
1271   command=(char *) NULL;
1272   status=MagickFalse;
1273   (void) CopyMagickString(output_filename,image_info->filename,MaxTextExtent);
1274   (void) CopyMagickString(input_filename,image->filename,MaxTextExtent);
1275   for (i=0; commands[i] != (char *) NULL; i++)
1276   {
1277     status=AcquireUniqueSymbolicLink(output_filename,image_info->filename);
1278     if( IfMagickFalse(AcquireUniqueFilename(image_info->unique)) )
1279       {
1280         ThrowFileException(exception,FileOpenError,
1281           "UnableToCreateTemporaryFile",image_info->unique);
1282         break;
1283       }
1284     if( IfMagickFalse(AcquireUniqueFilename(image_info->zero)) )
1285       {
1286         (void) RelinquishUniqueFileResource(image_info->unique);
1287         ThrowFileException(exception,FileOpenError,
1288           "UnableToCreateTemporaryFile",image_info->zero);
1289         break;
1290       }
1291     if (LocaleCompare(decode,"SCAN") != 0)
1292       {
1293         status=AcquireUniqueSymbolicLink(input_filename,image->filename);
1294         if( IfMagickFalse(status) )
1295           {
1296             ThrowFileException(exception,FileOpenError,
1297               "UnableToCreateTemporaryFile",input_filename);
1298             break;
1299           }
1300       }
1301     status=MagickFalse;
1302     command=InterpretImageProperties(image_info,image,commands[i],exception);
1303     if (command != (char *) NULL)
1304       {
1305         /*
1306           Execute delegate.
1307         */
1308         status=IsMagickTrue(ExternalDelegateCommand(delegate_info->spawn,
1309           image_info->verbose,command,(char *) NULL,exception) != 0);
1310         if (IfMagickTrue(delegate_info->spawn))
1311           {
1312             ssize_t
1313               count;
1314
1315             /*
1316               Wait for input file to 'disappear', or maximum 10 seconds.
1317             */
1318             count=100;
1319             while ((count-- > 0) && (access_utf8(image->filename,F_OK) == 0))
1320               (void) MagickDelay(100);  /* sleep 0.1 seconds */
1321           }
1322         command=DestroyString(command);
1323       }
1324     if (LocaleCompare(decode,"SCAN") != 0)
1325       {
1326         if( IfMagickFalse(CopyDelegateFile(image->filename,input_filename)) )
1327           (void) RelinquishUniqueFileResource(input_filename);
1328       }
1329     if( IfMagickFalse(CopyDelegateFile(image_info->filename,output_filename)) )
1330       (void) RelinquishUniqueFileResource(output_filename);
1331     if( IfMagickTrue(image_info->temporary) )
1332       (void) RelinquishUniqueFileResource(image_info->filename);
1333     (void) RelinquishUniqueFileResource(image_info->unique);
1334     (void) RelinquishUniqueFileResource(image_info->zero);
1335     (void) RelinquishUniqueFileResource(image_info->filename);
1336     (void) RelinquishUniqueFileResource(image->filename);
1337     if( IfMagickTrue(status) )
1338       {
1339         (void) ThrowMagickException(exception,GetMagickModule(),DelegateError,
1340           "DelegateFailed","`%s'",commands[i]);
1341         break;
1342       }
1343     commands[i]=DestroyString(commands[i]);
1344   }
1345   (void) CopyMagickString(image_info->filename,output_filename,MaxTextExtent);
1346   (void) CopyMagickString(image->filename,input_filename,MaxTextExtent);
1347   /*
1348     Relinquish resources.
1349   */
1350   for ( ; commands[i] != (char *) NULL; i++)
1351     commands[i]=DestroyString(commands[i]);
1352   commands=(char **) RelinquishMagickMemory(commands);
1353   if( IfMagickTrue(temporary) )
1354     (void) RelinquishUniqueFileResource(image->filename);
1355   return(IsMagickFalse(status));
1356 }
1357 \f
1358 /*
1359 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1360 %                                                                             %
1361 %                                                                             %
1362 %                                                                             %
1363 %  L i s t D e l e g a t e I n f o                                            %
1364 %                                                                             %
1365 %                                                                             %
1366 %                                                                             %
1367 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1368 %
1369 %  ListDelegateInfo() lists the image formats to a file.
1370 %
1371 %  The format of the ListDelegateInfo method is:
1372 %
1373 %      MagickBooleanType ListDelegateInfo(FILE *file,ExceptionInfo *exception)
1374 %
1375 %  A description of each parameter follows.
1376 %
1377 %    o file:  An pointer to a FILE.
1378 %
1379 %    o exception: return any errors or warnings in this structure.
1380 %
1381 */
1382 MagickExport MagickBooleanType ListDelegateInfo(FILE *file,
1383   ExceptionInfo *exception)
1384 {
1385   const DelegateInfo
1386     **delegate_info;
1387
1388   char
1389     **commands,
1390     delegate[MaxTextExtent];
1391
1392   const char
1393     *path;
1394
1395   register ssize_t
1396     i;
1397
1398   size_t
1399     number_delegates;
1400
1401   ssize_t
1402     j;
1403
1404   if (file == (const FILE *) NULL)
1405     file=stdout;
1406   delegate_info=GetDelegateInfoList("*",&number_delegates,exception);
1407   if (delegate_info == (const DelegateInfo **) NULL)
1408     return(MagickFalse);
1409   path=(const char *) NULL;
1410   for (i=0; i < (ssize_t) number_delegates; i++)
1411   {
1412     if( IfMagickTrue(delegate_info[i]->stealth) )
1413       continue;
1414     if ((path == (const char *) NULL) ||
1415         (LocaleCompare(path,delegate_info[i]->path) != 0))
1416       {
1417         if (delegate_info[i]->path != (char *) NULL)
1418           (void) FormatLocaleFile(file,"\nPath: %s\n\n",delegate_info[i]->path);
1419         (void) FormatLocaleFile(file,"Delegate                Command\n");
1420         (void) FormatLocaleFile(file,
1421           "-------------------------------------------------"
1422           "------------------------------\n");
1423       }
1424     path=delegate_info[i]->path;
1425     *delegate='\0';
1426     if (delegate_info[i]->encode != (char *) NULL)
1427       (void) CopyMagickString(delegate,delegate_info[i]->encode,MaxTextExtent);
1428     (void) ConcatenateMagickString(delegate,"        ",MaxTextExtent);
1429     delegate[8]='\0';
1430     commands=StringToList(delegate_info[i]->commands);
1431     if (commands == (char **) NULL)
1432       continue;
1433     (void) FormatLocaleFile(file,"%11s%c=%c%s  ",delegate_info[i]->decode ?
1434       delegate_info[i]->decode : "",delegate_info[i]->mode <= 0 ? '<' : ' ',
1435       delegate_info[i]->mode >= 0 ? '>' : ' ',delegate);
1436     StripString(commands[0]);
1437     (void) FormatLocaleFile(file,"\"%s\"\n",commands[0]);
1438     for (j=1; commands[j] != (char *) NULL; j++)
1439     {
1440       StripString(commands[j]);
1441       (void) FormatLocaleFile(file,"                     \"%s\"\n",commands[j]);
1442     }
1443     for (j=0; commands[j] != (char *) NULL; j++)
1444       commands[j]=DestroyString(commands[j]);
1445     commands=(char **) RelinquishMagickMemory(commands);
1446   }
1447   (void) fflush(file);
1448   delegate_info=(const DelegateInfo **)
1449     RelinquishMagickMemory((void *) delegate_info);
1450   return(MagickTrue);
1451 }
1452 \f
1453 /*
1454 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1455 %                                                                             %
1456 %                                                                             %
1457 %                                                                             %
1458 +   L o a d D e l e g a t e L i s t                                           %
1459 %                                                                             %
1460 %                                                                             %
1461 %                                                                             %
1462 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1463 %
1464 %  LoadDelegateCache() loads the delegate configurations which provides a
1465 %  mapping between delegate attributes and a delegate name.
1466 %
1467 %  The format of the LoadDelegateCache method is:
1468 %
1469 %      MagickBooleanType LoadDelegateCache(LinkedListInfo *delegate_cache,
1470 %        const char *xml,const char *filename,const size_t depth,
1471 %        ExceptionInfo *exception)
1472 %
1473 %  A description of each parameter follows:
1474 %
1475 %    o xml:  The delegate list in XML format.
1476 %
1477 %    o filename:  The delegate list filename.
1478 %
1479 %    o depth: depth of <include /> statements.
1480 %
1481 %    o exception: return any errors or warnings in this structure.
1482 %
1483 */
1484 static MagickBooleanType LoadDelegateCache(LinkedListInfo *delegate_cache,
1485   const char *xml,const char *filename,const size_t depth,
1486   ExceptionInfo *exception)
1487 {
1488   char
1489     keyword[MaxTextExtent],
1490     *token;
1491
1492   const char
1493     *q;
1494
1495   DelegateInfo
1496     *delegate_info;
1497
1498   MagickStatusType
1499     status;
1500
1501   /*
1502     Load the delegate map file.
1503   */
1504   (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1505     "Loading delegate configuration file \"%s\" ...",filename);
1506   if (xml == (const char *) NULL)
1507     return(MagickFalse);
1508   status=MagickTrue;
1509   delegate_info=(DelegateInfo *) NULL;
1510   token=AcquireString(xml);
1511   for (q=(const char *) xml; *q != '\0'; )
1512   {
1513     /*
1514       Interpret XML.
1515     */
1516     GetMagickToken(q,&q,token);
1517     if (*token == '\0')
1518       break;
1519     (void) CopyMagickString(keyword,token,MaxTextExtent);
1520     if (LocaleNCompare(keyword,"<!DOCTYPE",9) == 0)
1521       {
1522         /*
1523           Doctype element.
1524         */
1525         while ((LocaleNCompare(q,"]>",2) != 0) && (*q != '\0'))
1526           GetMagickToken(q,&q,token);
1527         continue;
1528       }
1529     if (LocaleNCompare(keyword,"<!--",4) == 0)
1530       {
1531         /*
1532           Comment element.
1533         */
1534         while ((LocaleNCompare(q,"->",2) != 0) && (*q != '\0'))
1535           GetMagickToken(q,&q,token);
1536         continue;
1537       }
1538     if (LocaleCompare(keyword,"<include") == 0)
1539       {
1540         /*
1541           Include element.
1542         */
1543         while (((*token != '/') && (*(token+1) != '>')) && (*q != '\0'))
1544         {
1545           (void) CopyMagickString(keyword,token,MaxTextExtent);
1546           GetMagickToken(q,&q,token);
1547           if (*token != '=')
1548             continue;
1549           GetMagickToken(q,&q,token);
1550           if (LocaleCompare(keyword,"file") == 0)
1551             {
1552               if (depth > 200)
1553                 (void) ThrowMagickException(exception,GetMagickModule(),
1554                   ConfigureError,"IncludeElementNestedTooDeeply","`%s'",token);
1555               else
1556                 {
1557                   char
1558                     path[MaxTextExtent],
1559                     *xml;
1560
1561                   GetPathComponent(filename,HeadPath,path);
1562                   if (*path != '\0')
1563                     (void) ConcatenateMagickString(path,DirectorySeparator,
1564                       MaxTextExtent);
1565                   if (*token == *DirectorySeparator)
1566                     (void) CopyMagickString(path,token,MaxTextExtent);
1567                   else
1568                     (void) ConcatenateMagickString(path,token,MaxTextExtent);
1569                   xml=FileToXML(path,~0UL);
1570                   if (xml != (char *) NULL)
1571                     {
1572                       status&=LoadDelegateCache(delegate_cache,xml,path,
1573                         depth+1,exception);
1574                       xml=(char *) RelinquishMagickMemory(xml);
1575                     }
1576                 }
1577             }
1578         }
1579         continue;
1580       }
1581     if (LocaleCompare(keyword,"<delegate") == 0)
1582       {
1583         /*
1584           Delegate element.
1585         */
1586         delegate_info=(DelegateInfo *) AcquireMagickMemory(
1587           sizeof(*delegate_info));
1588         if (delegate_info == (DelegateInfo *) NULL)
1589           ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1590         (void) ResetMagickMemory(delegate_info,0,sizeof(*delegate_info));
1591         delegate_info->path=ConstantString(filename);
1592         delegate_info->thread_support=MagickTrue;
1593         delegate_info->signature=MagickSignature;
1594         continue;
1595       }
1596     if (delegate_info == (DelegateInfo *) NULL)
1597       continue;
1598     if (LocaleCompare(keyword,"/>") == 0)
1599       {
1600         status=AppendValueToLinkedList(delegate_cache,delegate_info);
1601         if( IfMagickFalse(status) )
1602           (void) ThrowMagickException(exception,GetMagickModule(),
1603             ResourceLimitError,"MemoryAllocationFailed","`%s'",
1604             delegate_info->commands);
1605         delegate_info=(DelegateInfo *) NULL;
1606         continue;
1607       }
1608     GetMagickToken(q,(const char **) NULL,token);
1609     if (*token != '=')
1610       continue;
1611     GetMagickToken(q,&q,token);
1612     GetMagickToken(q,&q,token);
1613     switch (*keyword)
1614     {
1615       case 'C':
1616       case 'c':
1617       {
1618         if (LocaleCompare((char *) keyword,"command") == 0)
1619           {
1620             char
1621               *commands;
1622
1623             commands=AcquireString(token);
1624 #if defined(MAGICKCORE_WINDOWS_SUPPORT)
1625             if (strchr(commands,'@') != (char *) NULL)
1626               {
1627                 char
1628                   path[MaxTextExtent];
1629
1630                 NTGhostscriptEXE(path,MaxTextExtent);
1631                 (void) SubstituteString((char **) &commands,"@PSDelegate@",
1632                   path);
1633                 (void) SubstituteString((char **) &commands,"\\","/");
1634               }
1635 #endif
1636             (void) SubstituteString((char **) &commands,"&amp;","&");
1637             (void) SubstituteString((char **) &commands,"&quot;","\"");
1638             (void) SubstituteString((char **) &commands,"&gt;",">");
1639             (void) SubstituteString((char **) &commands,"&lt;","<");
1640             delegate_info->commands=commands;
1641             break;
1642           }
1643         break;
1644       }
1645       case 'D':
1646       case 'd':
1647       {
1648         if (LocaleCompare((char *) keyword,"decode") == 0)
1649           {
1650             delegate_info->decode=ConstantString(token);
1651             delegate_info->mode=1;
1652             break;
1653           }
1654         break;
1655       }
1656       case 'E':
1657       case 'e':
1658       {
1659         if (LocaleCompare((char *) keyword,"encode") == 0)
1660           {
1661             delegate_info->encode=ConstantString(token);
1662             delegate_info->mode=(-1);
1663             break;
1664           }
1665         break;
1666       }
1667       case 'M':
1668       case 'm':
1669       {
1670         if (LocaleCompare((char *) keyword,"mode") == 0)
1671           {
1672             delegate_info->mode=1;
1673             if (LocaleCompare(token,"bi") == 0)
1674               delegate_info->mode=0;
1675             else
1676               if (LocaleCompare(token,"encode") == 0)
1677                 delegate_info->mode=(-1);
1678             break;
1679           }
1680         break;
1681       }
1682       case 'S':
1683       case 's':
1684       {
1685         if (LocaleCompare((char *) keyword,"spawn") == 0)
1686           {
1687             delegate_info->spawn=IsStringTrue(token);
1688             break;
1689           }
1690         if (LocaleCompare((char *) keyword,"stealth") == 0)
1691           {
1692             delegate_info->stealth=IsStringTrue(token);
1693             break;
1694           }
1695         break;
1696       }
1697       case 'T':
1698       case 't':
1699       {
1700         if (LocaleCompare((char *) keyword,"thread-support") == 0)
1701           {
1702             delegate_info->thread_support=IsStringTrue(token);
1703             if (delegate_info->thread_support == MagickFalse)
1704               delegate_info->semaphore=AcquireSemaphoreInfo();
1705             break;
1706           }
1707         break;
1708       }
1709       default:
1710         break;
1711     }
1712   }
1713   token=(char *) RelinquishMagickMemory(token);
1714   return(status != 0 ? MagickTrue : MagickFalse);
1715 }