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-2021 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 %    https://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 
45 /*
46   Include declarations.
47 */
48 #include "MagickCore/studio.h"
49 #include "MagickCore/artifact.h"
50 #include "MagickCore/attribute.h"
51 #include "MagickCore/blob.h"
52 #include "MagickCore/client.h"
53 #include "MagickCore/configure.h"
54 #include "MagickCore/constitute.h"
55 #include "MagickCore/delegate.h"
56 #include "MagickCore/delegate-private.h"
57 #include "MagickCore/exception.h"
58 #include "MagickCore/exception-private.h"
59 #include "MagickCore/fx-private.h"
60 #include "MagickCore/image-private.h"
61 #include "MagickCore/linked-list.h"
62 #include "MagickCore/list.h"
63 #include "MagickCore/memory_.h"
64 #include "MagickCore/memory-private.h"
65 #include "MagickCore/nt-base-private.h"
66 #include "MagickCore/option.h"
67 #include "MagickCore/policy.h"
68 #include "MagickCore/property.h"
69 #include "MagickCore/resource_.h"
70 #include "MagickCore/semaphore.h"
71 #include "MagickCore/signature.h"
72 #include "MagickCore/string_.h"
73 #include "MagickCore/token.h"
74 #include "MagickCore/token-private.h"
75 #include "MagickCore/utility.h"
76 #include "MagickCore/utility-private.h"
77 #include "MagickCore/xml-tree.h"
78 #include "MagickCore/xml-tree-private.h"
79 
80 /*
81   Define declarations.
82 */
83 #define DelegateFilename  "delegates.xml"
84 
85 /*
86   Declare delegate map.
87 */
88 static const char
89   *DelegateMap = (const char *)
90     "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
91     "<delegatemap>"
92     "  <delegate decode=\"bpg\" command=\"&quot;bpgdec&quot; -b 16 -o &quot;%o.png&quot; &quot;%i&quot;; mv &quot;%o.png&quot; &quot;%o&quot;\"/>"
93     "  <delegate decode=\"png\" encode=\"bpg\" command=\"&quot;bpgenc&quot; -b 12 -q %~ -o &quot;%o&quot; &quot;%i&quot;\"/>"
94     "  <delegate decode=\"browse\" stealth=\"True\" spawn=\"True\" command=\"&quot;xdg-open&quot; https://imagemagick.org/; rm &quot;%i&quot;\"/>"
95     "  <delegate decode=\"cdr\" command=\"&quot;uniconvertor&quot; &quot;%i&quot; &quot;%o.svg&quot;; mv &quot;%o.svg&quot; &quot;%o&quot;\"/>"
96     "  <delegate decode=\"cgm\" command=\"&quot;uniconvertor&quot; &quot;%i&quot; &quot;%o.svg&quot;; mv &quot;%o.svg&quot; &quot;%o&quot;\"/>"
97     "  <delegate decode=\"https\" command=\"&quot;curl&quot; -s -k -L -o &quot;%o&quot; &quot;https:%M&quot;\"/>"
98     "  <delegate decode=\"doc\" command=\"&quot;soffice&quot; --convert-to pdf -outdir `dirname &quot;%i&quot;` &quot;%i&quot; 2&gt; &quot;%u&quot;; mv &quot;%i.pdf&quot; &quot;%o&quot;\"/>"
99     "  <delegate decode=\"docx\" command=\"&quot;soffice&quot; --convert-to pdf -outdir `dirname &quot;%i&quot;` &quot;%i&quot; 2&gt; &quot;%u&quot;; mv &quot;%i.pdf&quot; &quot;%o&quot;\"/>"
100     "  <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;\"/>"
101     "  <delegate decode=\"dot\" command=\"&quot;dot&quot; -Tsvg &quot;%i&quot; -o &quot;%o&quot;\"/>"
102     "  <delegate decode=\"dvi\" command=\"&quot;dvips&quot; -sstdout=%%stderr -o &quot;%o&quot; &quot;%i&quot;\"/>"
103     "  <delegate decode=\"dxf\" command=\"&quot;uniconvertor&quot; &quot;%i&quot; &quot;%o.svg&quot;; mv &quot;%o.svg&quot; &quot;%o&quot;\"/>"
104     "  <delegate decode=\"edit\" stealth=\"True\" command=\"&quot;xterm&quot; -title &quot;Edit Image Comment&quot; -e vi &quot;%o&quot;\"/>"
105     "  <delegate decode=\"eps\" encode=\"pdf\" mode=\"bi\" command=\"&quot;gs&quot; -sstdout=%%stderr -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 &quot;-sDEVICE=pdfwrite&quot; &quot;-sOutputFile=%o&quot; &quot;-f%i&quot;\"/>"
106     "  <delegate decode=\"eps\" encode=\"ps\" mode=\"bi\" command=\"&quot;gs&quot; -sstdout=%%stderr -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 &quot;-sDEVICE=ps2write&quot; &quot;-sOutputFile=%o&quot; &quot;-f%i&quot;\"/>"
107     "  <delegate decode=\"fig\" command=\"&quot;uniconvertor&quot; &quot;%i&quot; &quot;%o.svg&quot;; mv &quot;%o.svg&quot; &quot;%o&quot;\"/>"
108     "  <delegate decode=\"hpg\" command=\"&quot;hp2xx&quot; -sstdout=%%stderr -m eps -f `basename &quot;%o&quot;` &quot;%i&quot;;     mv -f `basename &quot;%o&quot;` &quot;%o&quot;\"/>"
109     "  <delegate decode=\"hpgl\" command=\"&quot;hp2xx&quot; -sstdout=%%stderr -m eps -f `basename &quot;%o&quot;` &quot;%i&quot;;     mv -f `basename &quot;%o&quot;` &quot;%o&quot;\"/>"
110     "  <delegate decode=\"htm\" command=\"&quot;html2ps&quot; -U -o &quot;%o&quot; &quot;%i&quot;\"/>"
111     "  <delegate decode=\"html\" command=\"&quot;html2ps&quot; -U -o &quot;%o&quot; &quot;%i&quot;\"/>"
112     "  <delegate decode=\"ilbm\" command=\"&quot;ilbmtoppm&quot; &quot;%i&quot; &gt; &quot;%o&quot;\"/>"
113     "  <delegate decode=\"jpg\" encode=\"lep\" mode=\"encode\" command=\"&quot;lepton&quot; &quot;%i&quot; &quot;%o&quot;\"/>"
114     "  <delegate decode=\"jxr\" command=\"mv &quot;%i&quot; &quot;%i.jxr&quot;; &quot;JxrDecApp&quot; -i &quot;%i.jxr&quot; -o &quot;%o.pnm&quot;; mv &quot;%i.jxr&quot; &quot;%i&quot;; mv &quot;%o.pnm&quot; &quot;%o&quot;\"/>"
115     "  <delegate decode=\"lep\" mode=\"decode\" command=\"&quot;lepton&quot; &quot;%i&quot; &quot;%o&quot;\"/>"
116     "  <delegate decode=\"odt\" command=\"&quot;soffice&quot; --convert-to pdf -outdir `dirname &quot;%i&quot;` &quot;%i&quot; 2&gt; &quot;%u&quot;; mv &quot;%i.pdf&quot; &quot;%o&quot;\"/>"
117     "  <delegate decode=\"pcl:cmyk\" stealth=\"True\" command=\"&quot;pcl6&quot; -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 &quot;-sDEVICE=pamcmyk32&quot; -dTextAlphaBits=%u -dGraphicsAlphaBits=%u &quot;-r%s&quot; %s &quot;-sOutputFile=%s&quot; &quot;%s&quot;\"/>"
118     "  <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;\"/>"
119     "  <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;\"/>"
120     "  <delegate decode=\"pdf\" encode=\"eps\" mode=\"bi\" command=\"&quot;gs&quot; -sstdout=%%stderr -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 -sPDFPassword=&quot;%a&quot; &quot;-sDEVICE=eps2write&quot; &quot;-sOutputFile=%o&quot; &quot;-f%i&quot;\"/>"
121     "  <delegate decode=\"pdf\" encode=\"ps\" mode=\"bi\" command=\"&quot;gs&quot; -sstdout=%%stderr -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 &quot;-sDEVICE=ps2write&quot; -sPDFPassword=&quot;%a&quot; &quot;-sOutputFile=%o&quot; &quot;-f%i&quot;\"/>"
122     "  <delegate decode=\"png\" encode=\"webp\" command=\"&quot;cwebp&quot; -quiet -q %Q &quot;%i&quot; -o &quot;%o&quot;\"/>"
123     "  <delegate decode=\"pnm\" encode=\"ilbm\" mode=\"encode\" command=\"&quot;ppmtoilbm&quot; -24if &quot;%i&quot; &gt; &quot;%o&quot;\"/>"
124     "  <delegate decode=\"bmp\" encode=\"jxr\" command=\"mv &quot;%i&quot; &quot;%i.bmp&quot;; &quot;JxrEncApp&quot; -i &quot;%i.bmp&quot; -o &quot;%o.jxr&quot;; mv &quot;%i.bmp&quot; &quot;%i&quot;; mv &quot;%o.jxr&quot; &quot;%o&quot;\"/>"
125     "  <delegate decode=\"bmp\" encode=\"wdp\" command=\"mv &quot;%i&quot; &quot;%i.bmp&quot;; &quot;JxrEncApp&quot; -i &quot;%i.bmp&quot; -o &quot;%o.jxr&quot;; mv &quot;%i.bmp&quot; &quot;%i&quot;; mv &quot;%o.jxr&quot; &quot;%o&quot;\"/>"
126     "  <delegate decode=\"ppt\" command=\"&quot;soffice&quot; --convert-to pdf -outdir `dirname &quot;%i&quot;` &quot;%i&quot; 2&gt; &quot;%u&quot;; mv &quot;%i.pdf&quot; &quot;%o&quot;\"/>"
127     "  <delegate decode=\"pptx\" command=\"&quot;soffice&quot; --convert-to pdf -outdir `dirname &quot;%i&quot;` &quot;%i&quot; 2&gt; &quot;%u&quot;; mv &quot;%i.pdf&quot; &quot;%o&quot;\"/>"
128     "  <delegate decode=\"ps\" encode=\"prt\" command=\"&quot;lpr&quot; &quot;%i&quot;\"/>"
129     "  <delegate decode=\"ps:alpha\" stealth=\"True\" command=\"&quot;gs&quot; -sstdout=%%stderr -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;\"/>"
130     "  <delegate decode=\"ps:cmyk\" stealth=\"True\" command=\"&quot;gs&quot; -sstdout=%%stderr -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 &quot;-sDEVICE=pamcmyk32&quot; -dTextAlphaBits=%u -dGraphicsAlphaBits=%u &quot;-r%s&quot; %s &quot;-sOutputFile=%s&quot; &quot;-f%s&quot; &quot;-f%s&quot;\"/>"
131     "  <delegate decode=\"ps:color\" stealth=\"True\" command=\"&quot;gs&quot; -sstdout=%%stderr -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;\"/>"
132     "  <delegate decode=\"ps\" encode=\"eps\" mode=\"bi\" command=\"&quot;gs&quot; -sstdout=%%stderr -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 &quot;-sDEVICE=eps2write&quot; &quot;-sOutputFile=%o&quot; &quot;-f%i&quot;\"/>"
133     "  <delegate decode=\"ps\" encode=\"pdf\" mode=\"bi\" command=\"&quot;gs&quot; -sstdout=%%stderr -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 &quot;-sDEVICE=pdfwrite&quot; &quot;-sOutputFile=%o&quot; &quot;-f%i&quot;\"/>"
134     "  <delegate decode=\"ps\" encode=\"print\" mode=\"encode\" command=\"lpr &quot;%i&quot;\"/>"
135     "  <delegate decode=\"ps:mono\" stealth=\"True\" command=\"&quot;gs&quot; -sstdout=%%stderr -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;-f%s&quot; &quot;-f%s&quot;\"/>"
136     "  <delegate decode=\"shtml\" command=\"&quot;html2ps&quot; -U -o &quot;%o&quot; &quot;%i&quot;\"/>"
137     "  <delegate decode=\"sid\" command=\"&quot;mrsidgeodecode&quot; -if sid -i &quot;%i&quot; -of tif -o &quot;%o&quot; &gt; &quot;%u&quot;\"/>"
138     "  <delegate decode=\"svg\" command=\"&quot;rsvg-convert&quot; -o &quot;%o&quot; &quot;%i&quot;\"/>"
139     "  <delegate decode=\"svg:decode\" stealth=\"True\" command=\"&quot;inkscape&quot; &quot;%s&quot; --export-png=&quot;%s&quot; --export-dpi=&quot;%s&quot; --export-background=&quot;%s&quot; --export-background-opacity=&quot;%s&quot; &gt; &quot;%s&quot; 2&gt;&amp;1\"/>"
140     "  <delegate decode=\"tiff\" encode=\"launch\" mode=\"encode\" command=\"&quot;gimp&quot; &quot;%i&quot;\"/>"
141     "  <delegate decode=\"wdp\" command=\"mv &quot;%i&quot; &quot;%i.jxr&quot;; &quot;JxrDecApp&quot; -i &quot;%i.jxr&quot; -o &quot;%o.bmp&quot;; mv &quot;%i.jxr&quot; &quot;%i&quot;; mv &quot;%o.bmp&quot; &quot;%o&quot;\"/>"
142     "  <delegate decode=\"webp\" command=\"&quot;dwebp&quot; -pam &quot;%i&quot; -o &quot;%o&quot;\"/>"
143     "  <delegate decode=\"xls\" command=\"&quot;soffice&quot; --convert-to pdf -outdir `dirname &quot;%i&quot;` &quot;%i&quot; 2&gt; &quot;%u&quot;; mv &quot;%i.pdf&quot; &quot;%o&quot;\"/>"
144     "  <delegate decode=\"xlsx\" command=\"&quot;soffice&quot; --convert-to pdf -outdir `dirname &quot;%i&quot;` &quot;%i&quot; 2&gt; &quot;%u&quot;; mv &quot;%i.pdf&quot; &quot;%o&quot;\"/>"
145     "  <delegate decode=\"xps:cmyk\" stealth=\"True\" command=\"&quot;gxps&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;\"/>"
146     "  <delegate decode=\"xps:color\" stealth=\"True\" command=\"&quot;gxps&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;\"/>"
147     "  <delegate decode=\"xps:mono\" stealth=\"True\" command=\"&quot;gxps&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;\"/>"
148     "  <delegate decode=\"mpeg:decode\" command=\"&quot;ffmpeg&quot; -v -1 -i &quot;%i&quot; -vframes %S -vcodec pam -an -f rawvideo -y &quot;%u.pam&quot; 2&gt; &quot;%u&quot;\"/>"
149     "  <delegate encode=\"mpeg:encode\" stealth=\"True\" command=\"&quot;ffmpeg&quot; -v -1 -i &quot;%M%%d.jpg&quot; -plays %I &quot;%u.%m&quot; 2&gt; &quot;%u&quot;\"/>"
150     "</delegatemap>";
151 
152 /*
153   Global declaractions.
154 */
155 static LinkedListInfo
156   *delegate_cache = (LinkedListInfo *) NULL;
157 
158 static SemaphoreInfo
159   *delegate_semaphore = (SemaphoreInfo *) NULL;
160 
161 /*
162   Forward declaractions.
163 */
164 static MagickBooleanType
165   IsDelegateCacheInstantiated(ExceptionInfo *),
166   LoadDelegateCache(LinkedListInfo *,const char *,const char *,const size_t,
167     ExceptionInfo *);
168 
169 /*
170 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
171 %                                                                             %
172 %                                                                             %
173 %                                                                             %
174 %  A c q u i r e D e l e g a t e C a c h e                                    %
175 %                                                                             %
176 %                                                                             %
177 %                                                                             %
178 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
179 %
180 %  AcquireDelegateCache() caches one or more delegate configurations which
181 %  provides a mapping between delegate attributes and a delegate name.
182 %
183 %  The format of the AcquireDelegateCache method is:
184 %
185 %      LinkedListInfo *AcquireDelegateCache(const char *filename,
186 %        ExceptionInfo *exception)
187 %
188 %  A description of each parameter follows:
189 %
190 %    o filename: the font file name.
191 %
192 %    o exception: return any errors or warnings in this structure.
193 %
194 */
AcquireDelegateCache(const char * filename,ExceptionInfo * exception)195 static LinkedListInfo *AcquireDelegateCache(const char *filename,
196   ExceptionInfo *exception)
197 {
198   LinkedListInfo
199     *cache;
200 
201   MagickStatusType
202     status;
203 
204   cache=NewLinkedList(0);
205   status=MagickTrue;
206 #if !MAGICKCORE_ZERO_CONFIGURATION_SUPPORT
207   {
208     const StringInfo
209       *option;
210 
211     LinkedListInfo
212       *options;
213 
214     options=GetConfigureOptions(filename,exception);
215     option=(const StringInfo *) GetNextValueInLinkedList(options);
216     while (option != (const StringInfo *) NULL)
217     {
218       status&=LoadDelegateCache(cache,(const char *)
219         GetStringInfoDatum(option),GetStringInfoPath(option),0,exception);
220       option=(const StringInfo *) GetNextValueInLinkedList(options);
221     }
222     options=DestroyConfigureOptions(options);
223   }
224 #endif
225   if (IsLinkedListEmpty(cache) != MagickFalse)
226     status&=LoadDelegateCache(cache,DelegateMap,"built-in",0,exception);
227   return(cache);
228 }
229 
230 /*
231 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
232 %                                                                             %
233 %                                                                             %
234 %                                                                             %
235 +   D e l e g a t e C o m p o n e n t G e n e s i s                           %
236 %                                                                             %
237 %                                                                             %
238 %                                                                             %
239 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
240 %
241 %  DelegateComponentGenesis() instantiates the delegate component.
242 %
243 %  The format of the DelegateComponentGenesis method is:
244 %
245 %      MagickBooleanType DelegateComponentGenesis(void)
246 %
247 */
DelegateComponentGenesis(void)248 MagickPrivate MagickBooleanType DelegateComponentGenesis(void)
249 {
250   if (delegate_semaphore == (SemaphoreInfo *) NULL)
251     delegate_semaphore=AcquireSemaphoreInfo();
252   return(MagickTrue);
253 }
254 
255 /*
256 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
257 %                                                                             %
258 %                                                                             %
259 %                                                                             %
260 %   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                         %
261 %                                                                             %
262 %                                                                             %
263 %                                                                             %
264 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
265 %
266 %  DelegateComponentTerminus() destroys the delegate component.
267 %
268 %  The format of the DelegateComponentTerminus method is:
269 %
270 %      DelegateComponentTerminus(void)
271 %
272 */
273 
DestroyDelegate(void * delegate_info)274 static void *DestroyDelegate(void *delegate_info)
275 {
276   DelegateInfo
277     *p;
278 
279   p=(DelegateInfo *) delegate_info;
280   if (p->path != (char *) NULL)
281     p->path=DestroyString(p->path);
282   if (p->decode != (char *) NULL)
283     p->decode=DestroyString(p->decode);
284   if (p->encode != (char *) NULL)
285     p->encode=DestroyString(p->encode);
286   if (p->commands != (char *) NULL)
287     p->commands=DestroyString(p->commands);
288   if (p->semaphore != (SemaphoreInfo *) NULL)
289     RelinquishSemaphoreInfo(&p->semaphore);
290   p=(DelegateInfo *) RelinquishMagickMemory(p);
291   return((void *) NULL);
292 }
293 
DelegateComponentTerminus(void)294 MagickPrivate void DelegateComponentTerminus(void)
295 {
296   if (delegate_semaphore == (SemaphoreInfo *) NULL)
297     ActivateSemaphoreInfo(&delegate_semaphore);
298   LockSemaphoreInfo(delegate_semaphore);
299   if (delegate_cache != (LinkedListInfo *) NULL)
300     delegate_cache=DestroyLinkedList(delegate_cache,DestroyDelegate);
301   UnlockSemaphoreInfo(delegate_semaphore);
302   RelinquishSemaphoreInfo(&delegate_semaphore);
303 }
304 
305 /*
306 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
307 %                                                                             %
308 %                                                                             %
309 %                                                                             %
310 +   E x t e r n a l D e l e g a t e C o m m a n d                             %
311 %                                                                             %
312 %                                                                             %
313 %                                                                             %
314 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
315 %
316 %  ExternalDelegateCommand() executes the specified command and waits until it
317 %  terminates.  The returned value is the exit status of the command.
318 %
319 %  The format of the ExternalDelegateCommand method is:
320 %
321 %      int ExternalDelegateCommand(const MagickBooleanType asynchronous,
322 %        const MagickBooleanType verbose,const char *command,
323 %        char *message,ExceptionInfo *exception)
324 %
325 %  A description of each parameter follows:
326 %
327 %    o asynchronous: a value other than 0 executes the parent program
328 %      concurrently with the new child process.
329 %
330 %    o verbose: a value other than 0 prints the executed command before it is
331 %      invoked.
332 %
333 %    o command: this string is the command to execute.
334 %
335 %    o message: an option buffer to receive any message posted to stdout or
336 %      stderr.
337 %
338 %    o exception: return any errors here.
339 %
340 */
ExternalDelegateCommand(const MagickBooleanType asynchronous,const MagickBooleanType verbose,const char * command,char * message,ExceptionInfo * exception)341 MagickExport int ExternalDelegateCommand(const MagickBooleanType asynchronous,
342   const MagickBooleanType verbose,const char *command,char *message,
343   ExceptionInfo *exception)
344 {
345   char
346     **arguments,
347     *sanitize_command;
348 
349   int
350     number_arguments,
351     status;
352 
353   PolicyDomain
354     domain;
355 
356   PolicyRights
357     rights;
358 
359   ssize_t
360     i;
361 
362   status=(-1);
363   arguments=StringToArgv(command,&number_arguments);
364   if (arguments == (char **) NULL)
365     return(status);
366   if (*arguments[1] == '\0')
367     {
368       for (i=0; i < (ssize_t) number_arguments; i++)
369         arguments[i]=DestroyString(arguments[i]);
370       arguments=(char **) RelinquishMagickMemory(arguments);
371       return(-1);
372     }
373   rights=ExecutePolicyRights;
374   domain=DelegatePolicyDomain;
375   if (IsRightsAuthorized(domain,rights,arguments[1]) == MagickFalse)
376     {
377       errno=EPERM;
378       (void) ThrowMagickException(exception,GetMagickModule(),PolicyError,
379         "NotAuthorized","`%s'",arguments[1]);
380       for (i=0; i < (ssize_t) number_arguments; i++)
381         arguments[i]=DestroyString(arguments[i]);
382       arguments=(char **) RelinquishMagickMemory(arguments);
383       return(-1);
384     }
385   if (verbose != MagickFalse)
386     {
387       (void) FormatLocaleFile(stderr,"%s\n",command);
388       (void) fflush(stderr);
389     }
390   sanitize_command=SanitizeString(command);
391   if (asynchronous != MagickFalse)
392     (void) ConcatenateMagickString(sanitize_command,"&",MagickPathExtent);
393   if (message != (char *) NULL)
394     *message='\0';
395 #if defined(MAGICKCORE_POSIX_SUPPORT)
396 #if defined(MAGICKCORE_HAVE_POPEN)
397   if ((asynchronous == MagickFalse) && (message !=  (char *) NULL))
398     {
399       char
400         buffer[MagickPathExtent];
401 
402       FILE
403         *file;
404 
405       size_t
406         offset;
407 
408       offset=0;
409       file=popen_utf8(sanitize_command,"r");
410       if (file == (FILE *) NULL)
411         status=system(sanitize_command);
412       else
413         {
414           while (fgets(buffer,(int) sizeof(buffer),file) != NULL)
415           {
416             size_t
417               length;
418 
419             length=MagickMin(MagickPathExtent-offset,strlen(buffer)+1);
420             if (length > 0)
421               {
422                 (void) CopyMagickString(message+offset,buffer,length);
423                 offset+=length-1;
424               }
425           }
426           status=pclose(file);
427         }
428     }
429   else
430 #endif
431     {
432 #if !defined(MAGICKCORE_HAVE_EXECVP)
433       status=system(sanitize_command);
434 #else
435       if ((asynchronous != MagickFalse) ||
436           (strpbrk(sanitize_command,"&;<>|") != (char *) NULL))
437         status=system(sanitize_command);
438       else
439         {
440           pid_t
441             child_pid;
442 
443           /*
444             Call application directly rather than from a shell.
445           */
446           child_pid=(pid_t) fork();
447           if (child_pid == (pid_t) -1)
448             status=system(sanitize_command);
449           else
450             if (child_pid == 0)
451               {
452                 status=execvp(arguments[1],arguments+1);
453                 _exit(1);
454               }
455             else
456               {
457                 int
458                   child_status;
459 
460                 pid_t
461                   pid;
462 
463                 child_status=0;
464                 pid=(pid_t) waitpid(child_pid,&child_status,0);
465                 if (pid == -1)
466                   status=(-1);
467                 else
468                   {
469                     if (WIFEXITED(child_status) != 0)
470                       status=WEXITSTATUS(child_status);
471                     else
472                       if (WIFSIGNALED(child_status))
473                         status=(-1);
474                   }
475               }
476         }
477 #endif
478     }
479 #elif defined(MAGICKCORE_WINDOWS_SUPPORT)
480   {
481     char
482       *p;
483 
484     /*
485       If a command shell is executed we need to change the forward slashes in
486       files to a backslash. We need to do this to keep Windows happy when we
487       want to 'move' a file.
488 
489       TODO: This won't work if one of the delegate parameters has a forward
490             slash as aparameter.
491     */
492     p=strstr(sanitize_command,"cmd.exe /c");
493     if (p != (char*) NULL)
494       {
495         p+=10;
496         for ( ; *p != '\0'; p++)
497           if (*p == '/')
498             *p=(*DirectorySeparator);
499       }
500   }
501   status=NTSystemCommand(sanitize_command,message);
502 #elif defined(vms)
503   status=system(sanitize_command);
504 #else
505 #  error No suitable system() method.
506 #endif
507   if (status < 0)
508     {
509       if ((message != (char *) NULL) && (*message != '\0'))
510         (void) ThrowMagickException(exception,GetMagickModule(),DelegateError,
511           "FailedToExecuteCommand","`%s' (%s)",sanitize_command,message);
512       else
513         (void) ThrowMagickException(exception,GetMagickModule(),DelegateError,
514           "FailedToExecuteCommand","`%s' (%d)",sanitize_command,status);
515     }
516   sanitize_command=DestroyString(sanitize_command);
517   for (i=0; i < (ssize_t) number_arguments; i++)
518     arguments[i]=DestroyString(arguments[i]);
519   arguments=(char **) RelinquishMagickMemory(arguments);
520   return(status);
521 }
522 
523 /*
524 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
525 %                                                                             %
526 %                                                                             %
527 %                                                                             %
528 %   G e t D e l e g a t e C o m m a n d                                       %
529 %                                                                             %
530 %                                                                             %
531 %                                                                             %
532 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
533 %
534 %  GetDelegateCommand() replaces any embedded formatting characters with the
535 %  appropriate image attribute and returns the resulting command.
536 %
537 %  The format of the GetDelegateCommand method is:
538 %
539 %      char *GetDelegateCommand(const ImageInfo *image_info,Image *image,
540 %        const char *decode,const char *encode,ExceptionInfo *exception)
541 %
542 %  A description of each parameter follows:
543 %
544 %    o command: Method GetDelegateCommand returns the command associated
545 %      with specified delegate tag.
546 %
547 %    o image_info: the image info.
548 %
549 %    o image: the image.
550 %
551 %    o decode: Specifies the decode delegate we are searching for as a
552 %      character string.
553 %
554 %    o encode: Specifies the encode delegate we are searching for as a
555 %      character string.
556 %
557 %    o exception: return any errors or warnings in this structure.
558 %
559 */
560 
GetMagickPropertyLetter(ImageInfo * image_info,Image * image,const char letter,ExceptionInfo * exception)561 static char *GetMagickPropertyLetter(ImageInfo *image_info,Image *image,
562   const char letter,ExceptionInfo *exception)
563 {
564 #define WarnNoImageReturn(format,letter) \
565   if (image == (Image *) NULL) \
566     { \
567       (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning, \
568         "NoImageForProperty",format,letter); \
569       break; \
570     }
571 #define WarnNoImageInfoReturn(format,letter) \
572   if (image_info == (ImageInfo *) NULL) \
573     { \
574       (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning, \
575         "NoImageInfoForProperty",format,letter); \
576       break; \
577     }
578 
579   char
580     value[MagickPathExtent];
581 
582   const char
583     *string;
584 
585   if ((image != (Image *) NULL) && (image->debug != MagickFalse))
586     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
587   else
588     if ((image_info != (ImageInfo *) NULL) &&
589         (image_info->debug != MagickFalse))
590       (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s","no-images");
591   /*
592     Get properties that are directly defined by images.
593   */
594   *value='\0';           /* formatted string */
595   string=(const char *) value;
596   switch (letter)
597   {
598     case 'a': /* authentication passphase */
599     {
600       WarnNoImageInfoReturn("\"%%%c\"",letter);
601       string=GetImageOption(image_info,"authenticate");
602       break;
603     }
604     case 'b':  /* image size read in - in bytes */
605     {
606       WarnNoImageReturn("\"%%%c\"",letter);
607       (void) FormatMagickSize(image->extent,MagickFalse,"B",MagickPathExtent,
608         value);
609       if (image->extent == 0)
610         (void) FormatMagickSize(GetBlobSize(image),MagickFalse,"B",
611           MagickPathExtent,value);
612       break;
613     }
614     case 'd':  /* Directory component of filename */
615     {
616       WarnNoImageReturn("\"%%%c\"",letter);
617       GetPathComponent(image->magick_filename,HeadPath,value);
618       break;
619     }
620     case 'e': /* Filename extension (suffix) of image file */
621     {
622       WarnNoImageReturn("\"%%%c\"",letter);
623       GetPathComponent(image->magick_filename,ExtensionPath,value);
624       break;
625     }
626     case 'f': /* Filename without directory component */
627     {
628       WarnNoImageReturn("\"%%%c\"",letter);
629       GetPathComponent(image->magick_filename,TailPath,value);
630       break;
631     }
632     case 'g': /* Image geometry, canvas and offset  %Wx%H+%X+%Y */
633     {
634       WarnNoImageReturn("\"%%%c\"",letter);
635       (void) FormatLocaleString(value,MagickPathExtent,
636         "%.20gx%.20g%+.20g%+.20g",(double) image->page.width,(double)
637         image->page.height,(double) image->page.x,(double) image->page.y);
638       break;
639     }
640     case 'h': /* Image height (current) */
641     {
642       WarnNoImageReturn("\"%%%c\"",letter);
643       (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
644         (image->rows != 0 ? image->rows : image->magick_rows));
645       break;
646     }
647     case 'i': /* Filename last used for an image (read or write) */
648     {
649       WarnNoImageReturn("\"%%%c\"",letter);
650       string=image->filename;
651       break;
652     }
653     case 'm': /* Image format (file magick) */
654     {
655       WarnNoImageReturn("\"%%%c\"",letter);
656       string=image->magick;
657       break;
658     }
659     case 'n': /* Number of images in the list.  */
660     {
661       if (image != (Image *) NULL)
662         (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
663           GetImageListLength(image));
664       break;
665     }
666     case 'o': /* Output Filename */
667     {
668       WarnNoImageInfoReturn("\"%%%c\"",letter);
669       string=image_info->filename;
670       break;
671     }
672     case 'p': /* Image index in current image list */
673     {
674       WarnNoImageReturn("\"%%%c\"",letter);
675       (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
676         GetImageIndexInList(image));
677       break;
678     }
679     case 'q': /* Quantum depth of image in memory */
680     {
681       WarnNoImageReturn("\"%%%c\"",letter);
682       (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
683         MAGICKCORE_QUANTUM_DEPTH);
684       break;
685     }
686     case 'r': /* Image storage class, colorspace, and alpha enabled.  */
687     {
688       ColorspaceType
689         colorspace;
690 
691       WarnNoImageReturn("\"%%%c\"",letter);
692       colorspace=image->colorspace;
693       if (SetImageGray(image,exception) != MagickFalse)
694         colorspace=GRAYColorspace;   /* FUTURE: this is IMv6 not IMv7 */
695       (void) FormatLocaleString(value,MagickPathExtent,"%s %s %s",
696         CommandOptionToMnemonic(MagickClassOptions,(ssize_t)
697         image->storage_class),CommandOptionToMnemonic(MagickColorspaceOptions,
698         (ssize_t) colorspace),image->alpha_trait != UndefinedPixelTrait ?
699         "Alpha" : "");
700       break;
701     }
702     case 's': /* Image scene number */
703     {
704       WarnNoImageReturn("\"%%%c\"",letter);
705       (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
706         image->scene);
707       break;
708     }
709     case 't': /* Base filename without directory or extention */
710     {
711       WarnNoImageReturn("\"%%%c\"",letter);
712       GetPathComponent(image->magick_filename,BasePath,value);
713       break;
714     }
715     case 'u': /* Unique filename */
716     {
717       WarnNoImageInfoReturn("\"%%%c\"",letter);
718       string=image_info->unique;
719       break;
720     }
721     case 'w': /* Image width (current) */
722     {
723       WarnNoImageReturn("\"%%%c\"",letter);
724       (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
725         (image->columns != 0 ? image->columns : image->magick_columns));
726       break;
727     }
728     case 'x': /* Image horizontal resolution (with units) */
729     {
730       WarnNoImageReturn("\"%%%c\"",letter);
731       (void) FormatLocaleString(value,MagickPathExtent,"%.20g",
732         fabs(image->resolution.x) > MagickEpsilon ? image->resolution.x :
733         image->units == PixelsPerCentimeterResolution ? DefaultResolution/2.54 :
734         DefaultResolution);
735       break;
736     }
737     case 'y': /* Image vertical resolution (with units) */
738     {
739       WarnNoImageReturn("\"%%%c\"",letter);
740       (void) FormatLocaleString(value,MagickPathExtent,"%.20g",
741         fabs(image->resolution.y) > MagickEpsilon ? image->resolution.y :
742         image->units == PixelsPerCentimeterResolution ? DefaultResolution/2.54 :
743         DefaultResolution);
744       break;
745     }
746     case 'z': /* Image depth as read in */
747     {
748       WarnNoImageReturn("\"%%%c\"",letter);
749       (void) FormatLocaleString(value,MagickPathExtent,"%.20g",
750         (double) image->depth);
751       break;
752     }
753     case 'A': /* Image alpha channel  */
754     {
755       WarnNoImageReturn("\"%%%c\"",letter);
756       string=CommandOptionToMnemonic(MagickPixelTraitOptions,(ssize_t)
757         image->alpha_trait);
758       break;
759     }
760     case 'C': /* Image compression method.  */
761     {
762       WarnNoImageReturn("\"%%%c\"",letter);
763       string=CommandOptionToMnemonic(MagickCompressOptions,
764         (ssize_t) image->compression);
765       break;
766     }
767     case 'D': /* Image dispose method.  */
768     {
769       WarnNoImageReturn("\"%%%c\"",letter);
770       string=CommandOptionToMnemonic(MagickDisposeOptions,
771         (ssize_t) image->dispose);
772       break;
773     }
774     case 'F':
775     {
776       /*
777         Magick filename - filename given incl. coder & read mods.
778       */
779       WarnNoImageReturn("\"%%%c\"",letter);
780       (void) CopyMagickString(value,image->magick_filename,MagickPathExtent);
781       break;
782     }
783     case 'G': /* Image size as geometry = "%wx%h" */
784     {
785       WarnNoImageReturn("\"%%%c\"",letter);
786       (void) FormatLocaleString(value,MagickPathExtent,"%.20gx%.20g",
787         (double) image->magick_columns,(double) image->magick_rows);
788       break;
789     }
790     case 'H': /* layer canvas height */
791     {
792       WarnNoImageReturn("\"%%%c\"",letter);
793       (void) FormatLocaleString(value,MagickPathExtent,"%.20g",
794         (double) image->page.height);
795       break;
796     }
797     case 'I': /* image iterations for animations */
798     {
799       WarnNoImageReturn("\"%%%c\"",letter);
800       (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
801         image->iterations);
802       break;
803     }
804     case 'M': /* Magick filename - filename given incl. coder & read mods */
805     {
806       WarnNoImageReturn("\"%%%c\"",letter);
807       string=image->magick_filename;
808       break;
809     }
810     case 'O': /* layer canvas offset with sign = "+%X+%Y" */
811     {
812       WarnNoImageReturn("\"%%%c\"",letter);
813       (void) FormatLocaleString(value,MagickPathExtent,"%+ld%+ld",(long)
814         image->page.x,(long) image->page.y);
815       break;
816     }
817     case 'P': /* layer canvas page size = "%Wx%H" */
818     {
819       WarnNoImageReturn("\"%%%c\"",letter);
820       (void) FormatLocaleString(value,MagickPathExtent,"%.20gx%.20g",
821         (double) image->page.width,(double) image->page.height);
822       break;
823     }
824     case '~': /* BPG image compression quality */
825     {
826       WarnNoImageReturn("\"%%%c\"",letter);
827       (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
828         (100-(image->quality == 0 ? 42 : image->quality))/2);
829       break;
830     }
831     case 'Q': /* image compression quality */
832     {
833       WarnNoImageReturn("\"%%%c\"",letter);
834       (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
835         (image->quality == 0 ? 92 : image->quality));
836       break;
837     }
838     case 'S': /* Number of scenes in image list.  */
839     {
840       WarnNoImageInfoReturn("\"%%%c\"",letter);
841       (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
842         (image_info->number_scenes == 0 ? 2147483647 :
843          image_info->number_scenes));
844       break;
845     }
846     case 'T': /* image time delay for animations */
847     {
848       WarnNoImageReturn("\"%%%c\"",letter);
849       (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
850         image->delay);
851       break;
852     }
853     case 'U': /* Image resolution units. */
854     {
855       WarnNoImageReturn("\"%%%c\"",letter);
856       string=CommandOptionToMnemonic(MagickResolutionOptions,
857         (ssize_t) image->units);
858       break;
859     }
860     case 'W': /* layer canvas width */
861     {
862       WarnNoImageReturn("\"%%%c\"",letter);
863       (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
864         image->page.width);
865       break;
866     }
867     case 'X': /* layer canvas X offset */
868     {
869       WarnNoImageReturn("\"%%%c\"",letter);
870       (void) FormatLocaleString(value,MagickPathExtent,"%+.20g",(double)
871         image->page.x);
872       break;
873     }
874     case 'Y': /* layer canvas Y offset */
875     {
876       WarnNoImageReturn("\"%%%c\"",letter);
877       (void) FormatLocaleString(value,MagickPathExtent,"%+.20g",(double)
878         image->page.y);
879       break;
880     }
881     case '%': /* percent escaped */
882     {
883       string="%";
884       break;
885     }
886     case '@': /* Trim bounding box, without actually trimming! */
887     {
888       RectangleInfo
889         page;
890 
891       WarnNoImageReturn("\"%%%c\"",letter);
892       page=GetImageBoundingBox(image,exception);
893       (void) FormatLocaleString(value,MagickPathExtent,
894         "%.20gx%.20g%+.20g%+.20g",(double) page.width,(double) page.height,
895         (double) page.x,(double) page.y);
896       break;
897     }
898     case '#':
899     {
900       /*
901         Image signature.
902       */
903       WarnNoImageReturn("\"%%%c\"",letter);
904       (void) SignatureImage(image,exception);
905       string=GetImageProperty(image,"signature",exception);
906       break;
907     }
908   }
909   return(SanitizeDelegateString(string));
910 }
911 
InterpretDelegateProperties(ImageInfo * image_info,Image * image,const char * embed_text,ExceptionInfo * exception)912 static char *InterpretDelegateProperties(ImageInfo *image_info,
913   Image *image,const char *embed_text,ExceptionInfo *exception)
914 {
915 #define ExtendInterpretText(string_length) \
916 { \
917   size_t length=(string_length); \
918   if ((size_t) (q-interpret_text+length+1) >= extent) \
919     { \
920       extent+=length; \
921       interpret_text=(char *) ResizeQuantumMemory(interpret_text,extent+ \
922         MagickPathExtent,sizeof(*interpret_text)); \
923       if (interpret_text == (char *) NULL) \
924         return((char *) NULL); \
925       q=interpret_text+strlen(interpret_text); \
926    } \
927 }
928 
929 #define AppendKeyValue2Text(key,value)\
930 { \
931   size_t length=strlen(key)+strlen(value)+2; \
932   if ((size_t) (q-interpret_text+length+1) >= extent) \
933     { \
934       extent+=length; \
935       interpret_text=(char *) ResizeQuantumMemory(interpret_text,extent+ \
936         MagickPathExtent,sizeof(*interpret_text)); \
937       if (interpret_text == (char *) NULL) \
938         return((char *) NULL); \
939       q=interpret_text+strlen(interpret_text); \
940      } \
941    q+=FormatLocaleString(q,extent,"%s=%s\n",(key),(value)); \
942 }
943 
944 #define AppendString2Text(string) \
945 { \
946   size_t length=strlen((string)); \
947   if ((size_t) (q-interpret_text+length+1) >= extent) \
948     { \
949       extent+=length; \
950       interpret_text=(char *) ResizeQuantumMemory(interpret_text,extent+ \
951         MagickPathExtent,sizeof(*interpret_text)); \
952       if (interpret_text == (char *) NULL) \
953         return((char *) NULL); \
954       q=interpret_text+strlen(interpret_text); \
955     } \
956   (void) CopyMagickString(q,(string),extent); \
957   q+=length; \
958 }
959 
960   char
961     *interpret_text,
962     *string;
963 
964   char
965     *q;  /* current position in interpret_text */
966 
967   const char
968     *p;  /* position in embed_text string being expanded */
969 
970   size_t
971     extent;  /* allocated length of interpret_text */
972 
973   MagickBooleanType
974     number;
975 
976   assert(image == NULL || image->signature == MagickCoreSignature);
977   assert(image_info == NULL || image_info->signature == MagickCoreSignature);
978   if ((image != (Image *) NULL) && (image->debug != MagickFalse))
979     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
980   else
981    if ((image_info != (ImageInfo *) NULL) && (image_info->debug != MagickFalse))
982      (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s","no-image");
983   if (embed_text == (const char *) NULL)
984     return(ConstantString(""));
985   p=embed_text;
986   while ((isspace((int) ((unsigned char) *p)) != 0) && (*p != '\0'))
987     p++;
988   if (*p == '\0')
989     return(ConstantString(""));
990   /*
991     Translate any embedded format characters.
992   */
993   interpret_text=AcquireString(embed_text);  /* new string with extra space */
994   extent=MagickPathExtent;  /* allocated space in string */
995   number=MagickFalse;  /* is last char a number? */
996   for (q=interpret_text; *p!='\0';
997     number=isdigit((int) ((unsigned char) *p)) ? MagickTrue : MagickFalse,p++)
998   {
999     /*
1000       Interpret escape characters (e.g. Filename: %M).
1001     */
1002     *q='\0';
1003     ExtendInterpretText(MagickPathExtent);
1004     switch (*p)
1005     {
1006       case '\\':
1007       {
1008         switch (*(p+1))
1009         {
1010           case '\0':
1011             continue;
1012           case 'r':  /* convert to RETURN */
1013           {
1014             *q++='\r';
1015             p++;
1016             continue;
1017           }
1018           case 'n':  /* convert to NEWLINE */
1019           {
1020             *q++='\n';
1021             p++;
1022             continue;
1023           }
1024           case '\n':  /* EOL removal UNIX,MacOSX */
1025           {
1026             p++;
1027             continue;
1028           }
1029           case '\r':  /* EOL removal DOS,Windows */
1030           {
1031             p++;
1032             if (*p == '\n') /* return-newline EOL */
1033               p++;
1034             continue;
1035           }
1036           default:
1037           {
1038             p++;
1039             *q++=(*p);
1040           }
1041         }
1042         continue;
1043       }
1044       case '&':
1045       {
1046         if (LocaleNCompare("&lt;",p,4) == 0)
1047           {
1048             *q++='<';
1049             p+=3;
1050           }
1051         else
1052           if (LocaleNCompare("&gt;",p,4) == 0)
1053             {
1054               *q++='>';
1055               p+=3;
1056             }
1057           else
1058             if (LocaleNCompare("&amp;",p,5) == 0)
1059               {
1060                 *q++='&';
1061                 p+=4;
1062               }
1063             else
1064               *q++=(*p);
1065         continue;
1066       }
1067       case '%':
1068         break;  /* continue to next set of handlers */
1069       default:
1070       {
1071         *q++=(*p);  /* any thing else is 'as normal' */
1072         continue;
1073       }
1074     }
1075     p++; /* advance beyond the percent */
1076     /*
1077       Doubled Percent - or percent at end of string.
1078     */
1079     if ((*p == '\0') || (*p == '\'') || (*p == '"'))
1080       p--;
1081     if (*p == '%')
1082       {
1083         *q++='%';
1084         continue;
1085       }
1086     /*
1087       Single letter escapes %c.
1088     */
1089     if (number != MagickFalse)
1090       {
1091         /*
1092           But only if not preceeded by a number!
1093         */
1094         *q++='%'; /* do NOT substitute the percent */
1095         p--;      /* back up one */
1096         continue;
1097       }
1098     string=GetMagickPropertyLetter(image_info,image,*p,exception);
1099     if (string != (char *) NULL)
1100       {
1101         AppendString2Text(string);
1102         string=DestroyString(string);
1103         continue;
1104       }
1105     (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
1106       "UnknownImageProperty","\"%%%c\"",*p);
1107   }
1108   *q='\0';
1109   return(interpret_text);
1110 }
1111 
GetDelegateCommand(const ImageInfo * image_info,Image * image,const char * decode,const char * encode,ExceptionInfo * exception)1112 MagickExport char *GetDelegateCommand(const ImageInfo *image_info,Image *image,
1113   const char *decode,const char *encode,ExceptionInfo *exception)
1114 {
1115   char
1116     *command,
1117     **commands;
1118 
1119   const DelegateInfo
1120     *delegate_info;
1121 
1122   ssize_t
1123     i;
1124 
1125   assert(image_info != (ImageInfo *) NULL);
1126   assert(image_info->signature == MagickCoreSignature);
1127   assert(image != (Image *) NULL);
1128   assert(image->signature == MagickCoreSignature);
1129   if (image->debug != MagickFalse)
1130     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1131 
1132   delegate_info=GetDelegateInfo(decode,encode,exception);
1133   if (delegate_info == (const DelegateInfo *) NULL)
1134     {
1135       (void) ThrowMagickException(exception,GetMagickModule(),DelegateError,
1136         "NoTagFound","`%s'",decode ? decode : encode);
1137       return((char *) NULL);
1138     }
1139   commands=StringToList(delegate_info->commands);
1140   if (commands == (char **) NULL)
1141     {
1142       (void) ThrowMagickException(exception,GetMagickModule(),
1143         ResourceLimitError,"MemoryAllocationFailed","`%s'",decode ? decode :
1144         encode);
1145       return((char *) NULL);
1146     }
1147   command=InterpretDelegateProperties((ImageInfo *) image_info,image,
1148     commands[0],exception);
1149   if (command == (char *) NULL)
1150     (void) ThrowMagickException(exception,GetMagickModule(),ResourceLimitError,
1151       "MemoryAllocationFailed","`%s'",commands[0]);
1152   /*
1153     Relinquish resources.
1154   */
1155   for (i=0; commands[i] != (char *) NULL; i++)
1156     commands[i]=DestroyString(commands[i]);
1157   commands=(char **) RelinquishMagickMemory(commands);
1158   return(command);
1159 }
1160 
1161 /*
1162 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1163 %                                                                             %
1164 %                                                                             %
1165 %                                                                             %
1166 %   G e t D e l e g a t e C o m m a n d s                                     %
1167 %                                                                             %
1168 %                                                                             %
1169 %                                                                             %
1170 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1171 %
1172 %  GetDelegateCommands() returns the commands associated with a delegate.
1173 %
1174 %  The format of the GetDelegateCommands method is:
1175 %
1176 %      const char *GetDelegateCommands(const DelegateInfo *delegate_info)
1177 %
1178 %  A description of each parameter follows:
1179 %
1180 %    o delegate_info:  The delegate info.
1181 %
1182 */
GetDelegateCommands(const DelegateInfo * delegate_info)1183 MagickExport const char *GetDelegateCommands(const DelegateInfo *delegate_info)
1184 {
1185   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1186 
1187   assert(delegate_info != (DelegateInfo *) NULL);
1188   assert(delegate_info->signature == MagickCoreSignature);
1189   return(delegate_info->commands);
1190 }
1191 
1192 /*
1193 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1194 %                                                                             %
1195 %                                                                             %
1196 %                                                                             %
1197 %   G e t D e l e g a t e I n f o                                             %
1198 %                                                                             %
1199 %                                                                             %
1200 %                                                                             %
1201 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1202 %
1203 %  GetDelegateInfo() returns any delegates associated with the specified tag.
1204 %
1205 %  The format of the GetDelegateInfo method is:
1206 %
1207 %      const DelegateInfo *GetDelegateInfo(const char *decode,
1208 %        const char *encode,ExceptionInfo *exception)
1209 %
1210 %  A description of each parameter follows:
1211 %
1212 %    o decode: Specifies the decode delegate we are searching for as a
1213 %      character string.
1214 %
1215 %    o encode: Specifies the encode delegate we are searching for as a
1216 %      character string.
1217 %
1218 %    o exception: return any errors or warnings in this structure.
1219 %
1220 */
GetDelegateInfo(const char * decode,const char * encode,ExceptionInfo * exception)1221 MagickExport const DelegateInfo *GetDelegateInfo(const char *decode,
1222   const char *encode,ExceptionInfo *exception)
1223 {
1224   const DelegateInfo
1225     *p;
1226 
1227   assert(exception != (ExceptionInfo *) NULL);
1228   if (IsDelegateCacheInstantiated(exception) == MagickFalse)
1229     return((const DelegateInfo *) NULL);
1230   /*
1231     Search for named delegate.
1232   */
1233   LockSemaphoreInfo(delegate_semaphore);
1234   ResetLinkedListIterator(delegate_cache);
1235   p=(const DelegateInfo *) GetNextValueInLinkedList(delegate_cache);
1236   if ((LocaleCompare(decode,"*") == 0) && (LocaleCompare(encode,"*") == 0))
1237     {
1238       UnlockSemaphoreInfo(delegate_semaphore);
1239       return(p);
1240     }
1241   while (p != (const DelegateInfo *) NULL)
1242   {
1243     if (p->mode > 0)
1244       {
1245         if (LocaleCompare(p->decode,decode) == 0)
1246           break;
1247         p=(const DelegateInfo *) GetNextValueInLinkedList(delegate_cache);
1248         continue;
1249       }
1250     if (p->mode < 0)
1251       {
1252         if (LocaleCompare(p->encode,encode) == 0)
1253           break;
1254         p=(const DelegateInfo *) GetNextValueInLinkedList(delegate_cache);
1255         continue;
1256       }
1257     if (LocaleCompare(decode,p->decode) == 0)
1258       if (LocaleCompare(encode,p->encode) == 0)
1259         break;
1260     if (LocaleCompare(decode,"*") == 0)
1261       if (LocaleCompare(encode,p->encode) == 0)
1262         break;
1263     if (LocaleCompare(decode,p->decode) == 0)
1264       if (LocaleCompare(encode,"*") == 0)
1265         break;
1266     p=(const DelegateInfo *) GetNextValueInLinkedList(delegate_cache);
1267   }
1268   if (p != (const DelegateInfo *) NULL)
1269     (void) InsertValueInLinkedList(delegate_cache,0,
1270       RemoveElementByValueFromLinkedList(delegate_cache,p));
1271   UnlockSemaphoreInfo(delegate_semaphore);
1272   return(p);
1273 }
1274 
1275 /*
1276 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1277 %                                                                             %
1278 %                                                                             %
1279 %                                                                             %
1280 %   G e t D e l e g a t e I n f o L i s t                                     %
1281 %                                                                             %
1282 %                                                                             %
1283 %                                                                             %
1284 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1285 %
1286 %  GetDelegateInfoList() returns any delegates that match the specified pattern.
1287 %
1288 %  The delegate of the GetDelegateInfoList function is:
1289 %
1290 %      const DelegateInfo **GetDelegateInfoList(const char *pattern,
1291 %        size_t *number_delegates,ExceptionInfo *exception)
1292 %
1293 %  A description of each parameter follows:
1294 %
1295 %    o pattern: Specifies a pointer to a text string containing a pattern.
1296 %
1297 %    o number_delegates:  This integer returns the number of delegates in the
1298 %      list.
1299 %
1300 %    o exception: return any errors or warnings in this structure.
1301 %
1302 */
1303 
1304 #if defined(__cplusplus) || defined(c_plusplus)
1305 extern "C" {
1306 #endif
1307 
DelegateInfoCompare(const void * x,const void * y)1308 static int DelegateInfoCompare(const void *x,const void *y)
1309 {
1310   const DelegateInfo
1311     **p,
1312     **q;
1313 
1314   int
1315     cmp;
1316 
1317   p=(const DelegateInfo **) x,
1318   q=(const DelegateInfo **) y;
1319   cmp=LocaleCompare((*p)->path,(*q)->path);
1320   if (cmp == 0)
1321     {
1322       if ((*p)->decode == (char *) NULL)
1323         if (((*p)->encode != (char *) NULL) &&
1324             ((*q)->encode != (char *) NULL))
1325           return(strcmp((*p)->encode,(*q)->encode));
1326       if (((*p)->decode != (char *) NULL) &&
1327           ((*q)->decode != (char *) NULL))
1328         return(strcmp((*p)->decode,(*q)->decode));
1329     }
1330   return(cmp);
1331 }
1332 
1333 #if defined(__cplusplus) || defined(c_plusplus)
1334 }
1335 #endif
1336 
GetDelegateInfoList(const char * pattern,size_t * number_delegates,ExceptionInfo * exception)1337 MagickExport const DelegateInfo **GetDelegateInfoList(const char *pattern,
1338   size_t *number_delegates,ExceptionInfo *exception)
1339 {
1340   const DelegateInfo
1341     **delegates;
1342 
1343   const DelegateInfo
1344     *p;
1345 
1346   ssize_t
1347     i;
1348 
1349   /*
1350     Allocate delegate list.
1351   */
1352   assert(number_delegates != (size_t *) NULL);
1353   assert(pattern != (char *) NULL);
1354   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",pattern);
1355 
1356   *number_delegates=0;
1357   p=GetDelegateInfo("*","*",exception);
1358   if (p == (const DelegateInfo *) NULL)
1359     return((const DelegateInfo **) NULL);
1360   delegates=(const DelegateInfo **) AcquireQuantumMemory((size_t)
1361     GetNumberOfElementsInLinkedList(delegate_cache)+1UL,sizeof(*delegates));
1362   if (delegates == (const DelegateInfo **) NULL)
1363     return((const DelegateInfo **) NULL);
1364   /*
1365     Generate delegate list.
1366   */
1367   LockSemaphoreInfo(delegate_semaphore);
1368   ResetLinkedListIterator(delegate_cache);
1369   p=(const DelegateInfo *) GetNextValueInLinkedList(delegate_cache);
1370   for (i=0; p != (const DelegateInfo *) NULL; )
1371   {
1372     if( (p->stealth == MagickFalse) &&
1373         ( GlobExpression(p->decode,pattern,MagickFalse) != MagickFalse ||
1374           GlobExpression(p->encode,pattern,MagickFalse) != MagickFalse) )
1375       delegates[i++]=p;
1376     p=(const DelegateInfo *) GetNextValueInLinkedList(delegate_cache);
1377   }
1378   UnlockSemaphoreInfo(delegate_semaphore);
1379   qsort((void *) delegates,(size_t) i,sizeof(*delegates),DelegateInfoCompare);
1380   delegates[i]=(DelegateInfo *) NULL;
1381   *number_delegates=(size_t) i;
1382   return(delegates);
1383 }
1384 
1385 /*
1386 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1387 %                                                                             %
1388 %                                                                             %
1389 %                                                                             %
1390 %   G e t D e l e g a t e L i s t                                             %
1391 %                                                                             %
1392 %                                                                             %
1393 %                                                                             %
1394 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1395 %
1396 %  GetDelegateList() returns any image format delegates that match the
1397 %  specified  pattern.
1398 %
1399 %  The format of the GetDelegateList function is:
1400 %
1401 %      char **GetDelegateList(const char *pattern,
1402 %        size_t *number_delegates,ExceptionInfo *exception)
1403 %
1404 %  A description of each parameter follows:
1405 %
1406 %    o pattern: Specifies a pointer to a text string containing a pattern.
1407 %
1408 %    o number_delegates:  This integer returns the number of delegates
1409 %      in the list.
1410 %
1411 %    o exception: return any errors or warnings in this structure.
1412 %
1413 */
1414 
1415 #if defined(__cplusplus) || defined(c_plusplus)
1416 extern "C" {
1417 #endif
1418 
DelegateCompare(const void * x,const void * y)1419 static int DelegateCompare(const void *x,const void *y)
1420 {
1421   const char
1422     **p,
1423     **q;
1424 
1425   p=(const char **) x;
1426   q=(const char **) y;
1427   return(LocaleCompare(*p,*q));
1428 }
1429 
1430 #if defined(__cplusplus) || defined(c_plusplus)
1431 }
1432 #endif
1433 
GetDelegateList(const char * pattern,size_t * number_delegates,ExceptionInfo * exception)1434 MagickExport char **GetDelegateList(const char *pattern,
1435   size_t *number_delegates,ExceptionInfo *exception)
1436 {
1437   char
1438     **delegates;
1439 
1440   const DelegateInfo
1441     *p;
1442 
1443   ssize_t
1444     i;
1445 
1446   /*
1447     Allocate delegate list.
1448   */
1449   assert(pattern != (char *) NULL);
1450   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",pattern);
1451 
1452   assert(number_delegates != (size_t *) NULL);
1453   *number_delegates=0;
1454   p=GetDelegateInfo("*","*",exception);
1455   if (p == (const DelegateInfo *) NULL)
1456     return((char **) NULL);
1457   delegates=(char **) AcquireQuantumMemory((size_t)
1458     GetNumberOfElementsInLinkedList(delegate_cache)+1UL,sizeof(*delegates));
1459   if (delegates == (char **) NULL)
1460     return((char **) NULL);
1461   LockSemaphoreInfo(delegate_semaphore);
1462   ResetLinkedListIterator(delegate_cache);
1463   p=(const DelegateInfo *) GetNextValueInLinkedList(delegate_cache);
1464   for (i=0; p != (const DelegateInfo *) NULL; )
1465   {
1466     if( (p->stealth == MagickFalse) &&
1467         GlobExpression(p->decode,pattern,MagickFalse) != MagickFalse )
1468       delegates[i++]=ConstantString(p->decode);
1469     if( (p->stealth == MagickFalse) &&
1470         GlobExpression(p->encode,pattern,MagickFalse) != MagickFalse )
1471       delegates[i++]=ConstantString(p->encode);
1472     p=(const DelegateInfo *) GetNextValueInLinkedList(delegate_cache);
1473   }
1474   UnlockSemaphoreInfo(delegate_semaphore);
1475   qsort((void *) delegates,(size_t) i,sizeof(*delegates),DelegateCompare);
1476   delegates[i]=(char *) NULL;
1477   *number_delegates=(size_t) i;
1478   return(delegates);
1479 }
1480 
1481 /*
1482 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1483 %                                                                             %
1484 %                                                                             %
1485 %                                                                             %
1486 %   G e t D e l e g a t e M o d e                                             %
1487 %                                                                             %
1488 %                                                                             %
1489 %                                                                             %
1490 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1491 %
1492 %  GetDelegateMode() returns the mode of the delegate.
1493 %
1494 %  The format of the GetDelegateMode method is:
1495 %
1496 %      ssize_t GetDelegateMode(const DelegateInfo *delegate_info)
1497 %
1498 %  A description of each parameter follows:
1499 %
1500 %    o delegate_info:  The delegate info.
1501 %
1502 */
GetDelegateMode(const DelegateInfo * delegate_info)1503 MagickExport ssize_t GetDelegateMode(const DelegateInfo *delegate_info)
1504 {
1505   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1506 
1507   assert(delegate_info != (DelegateInfo *) NULL);
1508   assert(delegate_info->signature == MagickCoreSignature);
1509   return(delegate_info->mode);
1510 }
1511 
1512 /*
1513 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1514 %                                                                             %
1515 %                                                                             %
1516 %                                                                             %
1517 +   G e t D e l e g a t e T h r e a d S u p p o r t                           %
1518 %                                                                             %
1519 %                                                                             %
1520 %                                                                             %
1521 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1522 %
1523 %  GetDelegateThreadSupport() returns MagickTrue if the delegate supports
1524 %  threads.
1525 %
1526 %  The format of the GetDelegateThreadSupport method is:
1527 %
1528 %      MagickBooleanType GetDelegateThreadSupport(
1529 %        const DelegateInfo *delegate_info)
1530 %
1531 %  A description of each parameter follows:
1532 %
1533 %    o delegate_info:  The delegate info.
1534 %
1535 */
GetDelegateThreadSupport(const DelegateInfo * delegate_info)1536 MagickExport MagickBooleanType GetDelegateThreadSupport(
1537   const DelegateInfo *delegate_info)
1538 {
1539   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1540 
1541   assert(delegate_info != (DelegateInfo *) NULL);
1542   assert(delegate_info->signature == MagickCoreSignature);
1543   return(delegate_info->thread_support);
1544 }
1545 
1546 /*
1547 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1548 %                                                                             %
1549 %                                                                             %
1550 %                                                                             %
1551 +   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                     %
1552 %                                                                             %
1553 %                                                                             %
1554 %                                                                             %
1555 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1556 %
1557 %  IsDelegateCacheInstantiated() determines if the delegate cache is
1558 %  instantiated.  If not, it instantiates the cache and returns it.
1559 %
1560 %  The format of the IsDelegateInstantiated method is:
1561 %
1562 %      MagickBooleanType IsDelegateCacheInstantiated(ExceptionInfo *exception)
1563 %
1564 %  A description of each parameter follows.
1565 %
1566 %    o exception: return any errors or warnings in this structure.
1567 %
1568 */
IsDelegateCacheInstantiated(ExceptionInfo * exception)1569 static MagickBooleanType IsDelegateCacheInstantiated(ExceptionInfo *exception)
1570 {
1571   if (delegate_cache == (LinkedListInfo *) NULL)
1572     {
1573       if (delegate_semaphore == (SemaphoreInfo *) NULL)
1574         ActivateSemaphoreInfo(&delegate_semaphore);
1575       LockSemaphoreInfo(delegate_semaphore);
1576       if (delegate_cache == (LinkedListInfo *) NULL)
1577         delegate_cache=AcquireDelegateCache(DelegateFilename,exception);
1578       UnlockSemaphoreInfo(delegate_semaphore);
1579     }
1580   return(delegate_cache != (LinkedListInfo *) NULL ? MagickTrue : MagickFalse);
1581 }
1582 
1583 /*
1584 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1585 %                                                                             %
1586 %                                                                             %
1587 %                                                                             %
1588 %   I n v o k e D e l e g a t e                                               %
1589 %                                                                             %
1590 %                                                                             %
1591 %                                                                             %
1592 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1593 %
1594 %  InvokeDelegate replaces any embedded formatting characters with the
1595 %  appropriate image attribute and executes the resulting command.  MagickFalse
1596 %  is returned if the commands execute with success otherwise MagickTrue.
1597 %
1598 %  The format of the InvokeDelegate method is:
1599 %
1600 %      MagickBooleanType InvokeDelegate(ImageInfo *image_info,Image *image,
1601 %        const char *decode,const char *encode,ExceptionInfo *exception)
1602 %
1603 %  A description of each parameter follows:
1604 %
1605 %    o image_info: the imageInfo.
1606 %
1607 %    o image: the image.
1608 %
1609 %    o exception: return any errors or warnings in this structure.
1610 %
1611 */
1612 
CopyDelegateFile(const char * source,const char * destination,const MagickBooleanType overwrite)1613 static MagickBooleanType CopyDelegateFile(const char *source,
1614   const char *destination,const MagickBooleanType overwrite)
1615 {
1616   int
1617     destination_file,
1618     source_file;
1619 
1620   MagickBooleanType
1621     status;
1622 
1623   size_t
1624     i;
1625 
1626   size_t
1627     length,
1628     quantum;
1629 
1630   ssize_t
1631     count;
1632 
1633   struct stat
1634     attributes;
1635 
1636   unsigned char
1637     *buffer;
1638 
1639   /*
1640     Copy source file to destination.
1641   */
1642   assert(source != (const char *) NULL);
1643   assert(destination != (char *) NULL);
1644   if (overwrite == MagickFalse)
1645     {
1646       status=GetPathAttributes(destination,&attributes);
1647       if (status != MagickFalse)
1648         return(MagickTrue);
1649     }
1650   destination_file=open_utf8(destination,O_WRONLY | O_BINARY | O_CREAT,S_MODE);
1651   if (destination_file == -1)
1652     return(MagickFalse);
1653   source_file=open_utf8(source,O_RDONLY | O_BINARY,0);
1654   if (source_file == -1)
1655     {
1656       (void) close(destination_file);
1657       return(MagickFalse);
1658     }
1659   quantum=(size_t) MagickMaxBufferExtent;
1660   if ((fstat(source_file,&attributes) == 0) && (attributes.st_size > 0))
1661     quantum=MagickMin((size_t) attributes.st_size,MagickMaxBufferExtent);
1662   buffer=(unsigned char *) AcquireQuantumMemory(quantum,sizeof(*buffer));
1663   if (buffer == (unsigned char *) NULL)
1664     {
1665       (void) close(source_file);
1666       (void) close(destination_file);
1667       return(MagickFalse);
1668     }
1669   length=0;
1670   for (i=0; ; i+=count)
1671   {
1672     count=(ssize_t) read(source_file,buffer,quantum);
1673     if (count <= 0)
1674       break;
1675     length=(size_t) count;
1676     count=(ssize_t) write(destination_file,buffer,length);
1677     if ((size_t) count != length)
1678       break;
1679   }
1680   (void) close(destination_file);
1681   (void) close(source_file);
1682   buffer=(unsigned char *) RelinquishMagickMemory(buffer);
1683   return(i != 0 ? MagickTrue : MagickFalse);
1684 }
1685 
InvokeDelegate(ImageInfo * image_info,Image * image,const char * decode,const char * encode,ExceptionInfo * exception)1686 MagickExport MagickBooleanType InvokeDelegate(ImageInfo *image_info,
1687   Image *image,const char *decode,const char *encode,ExceptionInfo *exception)
1688 {
1689   char
1690     *command,
1691     **commands,
1692     input_filename[MagickPathExtent],
1693     output_filename[MagickPathExtent];
1694 
1695   const DelegateInfo
1696     *delegate_info;
1697 
1698   MagickBooleanType
1699     status,
1700     temporary;
1701 
1702   PolicyRights
1703     rights;
1704 
1705   ssize_t
1706     i;
1707 
1708   /*
1709     Get delegate.
1710   */
1711   assert(image_info != (ImageInfo *) NULL);
1712   assert(image_info->signature == MagickCoreSignature);
1713   assert(image != (Image *) NULL);
1714   assert(image->signature == MagickCoreSignature);
1715   if (image->debug != MagickFalse)
1716     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1717   rights=ExecutePolicyRights;
1718   if (IsRightsAuthorized(DelegatePolicyDomain,rights,decode) == MagickFalse)
1719     {
1720       errno=EPERM;
1721       (void) ThrowMagickException(exception,GetMagickModule(),PolicyError,
1722         "NotAuthorized","`%s'",decode);
1723       return(MagickFalse);
1724     }
1725   if (IsRightsAuthorized(DelegatePolicyDomain,rights,encode) == MagickFalse)
1726     {
1727       errno=EPERM;
1728       (void) ThrowMagickException(exception,GetMagickModule(),PolicyError,
1729         "NotAuthorized","`%s'",encode);
1730       return(MagickFalse);
1731     }
1732   temporary=*image->filename == '\0' ? MagickTrue : MagickFalse;
1733   if ((temporary != MagickFalse) && (AcquireUniqueFilename(image->filename) ==
1734       MagickFalse))
1735     {
1736       ThrowFileException(exception,FileOpenError,"UnableToCreateTemporaryFile",
1737         image->filename);
1738       return(MagickFalse);
1739     }
1740   delegate_info=GetDelegateInfo(decode,encode,exception);
1741   if (delegate_info == (DelegateInfo *) NULL)
1742     {
1743       if (temporary != MagickFalse)
1744         (void) RelinquishUniqueFileResource(image->filename);
1745       (void) ThrowMagickException(exception,GetMagickModule(),DelegateError,
1746         "NoTagFound","`%s'",decode ? decode : encode);
1747       return(MagickFalse);
1748     }
1749   if (*image_info->filename == '\0')
1750     {
1751       if (AcquireUniqueFilename(image_info->filename) == MagickFalse)
1752         {
1753           if (temporary != MagickFalse)
1754             (void) RelinquishUniqueFileResource(image->filename);
1755           ThrowFileException(exception,FileOpenError,
1756             "UnableToCreateTemporaryFile",image_info->filename);
1757           return(MagickFalse);
1758         }
1759       image_info->temporary=MagickTrue;
1760     }
1761   if ((delegate_info->mode != 0) && (((decode != (const char *) NULL) &&
1762         (delegate_info->encode != (char *) NULL)) ||
1763        ((encode != (const char *) NULL) &&
1764         (delegate_info->decode != (char *) NULL))))
1765     {
1766       char
1767         *magick;
1768 
1769       ImageInfo
1770         *clone_info;
1771 
1772       Image
1773         *p;
1774 
1775       /*
1776         Delegate requires a particular image format.
1777       */
1778       if (AcquireUniqueFilename(image_info->unique) == MagickFalse)
1779         {
1780           ThrowFileException(exception,FileOpenError,
1781             "UnableToCreateTemporaryFile",image_info->unique);
1782           return(MagickFalse);
1783         }
1784       magick=InterpretImageProperties(image_info,image,decode != (char *) NULL ?
1785         delegate_info->encode : delegate_info->decode,exception);
1786       if (magick == (char *) NULL)
1787         {
1788           (void) RelinquishUniqueFileResource(image_info->unique);
1789           if (temporary != MagickFalse)
1790             (void) RelinquishUniqueFileResource(image->filename);
1791           (void) ThrowMagickException(exception,GetMagickModule(),
1792             DelegateError,"DelegateFailed","`%s'",decode ? decode : encode);
1793           return(MagickFalse);
1794         }
1795       LocaleUpper(magick);
1796       clone_info=CloneImageInfo(image_info);
1797       (void) CopyMagickString((char *) clone_info->magick,magick,
1798         MagickPathExtent);
1799       if (LocaleCompare(magick,"NULL") != 0)
1800         (void) CopyMagickString(image->magick,magick,MagickPathExtent);
1801       magick=DestroyString(magick);
1802       (void) FormatLocaleString(clone_info->filename,MagickPathExtent,"%s:",
1803         delegate_info->decode);
1804       (void) SetImageInfo(clone_info,(unsigned int) GetImageListLength(image),
1805         exception);
1806       (void) CopyMagickString(clone_info->filename,image_info->filename,
1807         MagickPathExtent);
1808       (void) CopyMagickString(image_info->filename,image->filename,
1809         MagickPathExtent);
1810       for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
1811       {
1812         (void) FormatLocaleString(p->filename,MagickPathExtent,"%s:%s",
1813           delegate_info->decode,clone_info->filename);
1814         status=WriteImage(clone_info,p,exception);
1815         if (status == MagickFalse)
1816           {
1817             (void) RelinquishUniqueFileResource(image_info->unique);
1818             if (temporary != MagickFalse)
1819               (void) RelinquishUniqueFileResource(image->filename);
1820             clone_info=DestroyImageInfo(clone_info);
1821             (void) ThrowMagickException(exception,GetMagickModule(),
1822               DelegateError,"DelegateFailed","`%s'",decode ? decode : encode);
1823             return(MagickFalse);
1824           }
1825         if (clone_info->adjoin != MagickFalse)
1826           break;
1827       }
1828       (void) RelinquishUniqueFileResource(image_info->unique);
1829       clone_info=DestroyImageInfo(clone_info);
1830     }
1831   /*
1832     Invoke delegate.
1833   */
1834   commands=StringToList(delegate_info->commands);
1835   if (commands == (char **) NULL)
1836     {
1837       if (temporary != MagickFalse)
1838         (void) RelinquishUniqueFileResource(image->filename);
1839       (void) ThrowMagickException(exception,GetMagickModule(),
1840         ResourceLimitError,"MemoryAllocationFailed","`%s'",
1841         decode ? decode : encode);
1842       return(MagickFalse);
1843     }
1844   command=(char *) NULL;
1845   status=MagickTrue;
1846   (void) CopyMagickString(output_filename,image_info->filename,
1847     MagickPathExtent);
1848   (void) CopyMagickString(input_filename,image->filename,MagickPathExtent);
1849   for (i=0; commands[i] != (char *) NULL; i++)
1850   {
1851     (void) AcquireUniqueSymbolicLink(output_filename,image_info->filename);
1852     if (AcquireUniqueFilename(image_info->unique) == MagickFalse)
1853       {
1854         ThrowFileException(exception,FileOpenError,
1855           "UnableToCreateTemporaryFile",image_info->unique);
1856         break;
1857       }
1858     if (LocaleCompare(decode,"SCAN") != 0)
1859       {
1860         status=AcquireUniqueSymbolicLink(input_filename,image->filename);
1861         if (status == MagickFalse)
1862           {
1863             ThrowFileException(exception,FileOpenError,
1864               "UnableToCreateTemporaryFile",input_filename);
1865             break;
1866           }
1867       }
1868     status=MagickTrue;
1869     command=InterpretDelegateProperties(image_info,image,commands[i],exception);
1870     if (command != (char *) NULL)
1871       {
1872         /*
1873           Execute delegate.
1874         */
1875         if (ExternalDelegateCommand(delegate_info->spawn,image_info->verbose,
1876           command,(char *) NULL,exception) != 0)
1877           status=MagickFalse;
1878         if (delegate_info->spawn != MagickFalse)
1879           {
1880             ssize_t
1881               count;
1882 
1883             /*
1884               Wait for input file to 'disappear', or maximum 2 seconds.
1885             */
1886             count=20;
1887             while ((count-- > 0) && (access_utf8(image->filename,F_OK) == 0))
1888               (void) MagickDelay(100);  /* sleep 0.1 seconds */
1889           }
1890         command=DestroyString(command);
1891       }
1892     if (LocaleCompare(decode,"SCAN") != 0)
1893       {
1894         if (CopyDelegateFile(image->filename,input_filename,MagickFalse) == MagickFalse)
1895           (void) RelinquishUniqueFileResource(input_filename);
1896       }
1897     if ((strcmp(input_filename,output_filename) != 0) &&
1898         (CopyDelegateFile(image_info->filename,output_filename,MagickTrue) == MagickFalse))
1899       (void) RelinquishUniqueFileResource(output_filename);
1900     if (image_info->temporary != MagickFalse)
1901       (void) RelinquishUniqueFileResource(image_info->filename);
1902     (void) RelinquishUniqueFileResource(image_info->unique);
1903     (void) RelinquishUniqueFileResource(image_info->filename);
1904     (void) RelinquishUniqueFileResource(image->filename);
1905     if (status == MagickFalse)
1906       {
1907         (void) ThrowMagickException(exception,GetMagickModule(),DelegateError,
1908           "DelegateFailed","`%s'",commands[i]);
1909         break;
1910       }
1911     commands[i]=DestroyString(commands[i]);
1912   }
1913   (void) CopyMagickString(image_info->filename,output_filename,
1914     MagickPathExtent);
1915   (void) CopyMagickString(image->filename,input_filename,MagickPathExtent);
1916   /*
1917     Relinquish resources.
1918   */
1919   for ( ; commands[i] != (char *) NULL; i++)
1920     commands[i]=DestroyString(commands[i]);
1921   commands=(char **) RelinquishMagickMemory(commands);
1922   if (temporary != MagickFalse)
1923     (void) RelinquishUniqueFileResource(image->filename);
1924   return(status);
1925 }
1926 
1927 /*
1928 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1929 %                                                                             %
1930 %                                                                             %
1931 %                                                                             %
1932 %  L i s t D e l e g a t e I n f o                                            %
1933 %                                                                             %
1934 %                                                                             %
1935 %                                                                             %
1936 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1937 %
1938 %  ListDelegateInfo() lists the image formats to a file.
1939 %
1940 %  The format of the ListDelegateInfo method is:
1941 %
1942 %      MagickBooleanType ListDelegateInfo(FILE *file,ExceptionInfo *exception)
1943 %
1944 %  A description of each parameter follows.
1945 %
1946 %    o file:  An pointer to a FILE.
1947 %
1948 %    o exception: return any errors or warnings in this structure.
1949 %
1950 */
ListDelegateInfo(FILE * file,ExceptionInfo * exception)1951 MagickExport MagickBooleanType ListDelegateInfo(FILE *file,
1952   ExceptionInfo *exception)
1953 {
1954   const DelegateInfo
1955     **delegate_info;
1956 
1957   char
1958     **commands,
1959     delegate[MagickPathExtent];
1960 
1961   const char
1962     *path;
1963 
1964   ssize_t
1965     i;
1966 
1967   size_t
1968     number_delegates;
1969 
1970   ssize_t
1971     j;
1972 
1973   if (file == (const FILE *) NULL)
1974     file=stdout;
1975   delegate_info=GetDelegateInfoList("*",&number_delegates,exception);
1976   if (delegate_info == (const DelegateInfo **) NULL)
1977     return(MagickFalse);
1978   path=(const char *) NULL;
1979   for (i=0; i < (ssize_t) number_delegates; i++)
1980   {
1981     if (delegate_info[i]->stealth != MagickFalse)
1982       continue;
1983     if ((path == (const char *) NULL) ||
1984         (LocaleCompare(path,delegate_info[i]->path) != 0))
1985       {
1986         if (delegate_info[i]->path != (char *) NULL)
1987           (void) FormatLocaleFile(file,"\nPath: %s\n\n",delegate_info[i]->path);
1988         (void) FormatLocaleFile(file,"Delegate                Command\n");
1989         (void) FormatLocaleFile(file,
1990           "-------------------------------------------------"
1991           "------------------------------\n");
1992       }
1993     path=delegate_info[i]->path;
1994     *delegate='\0';
1995     if (delegate_info[i]->encode != (char *) NULL)
1996       (void) CopyMagickString(delegate,delegate_info[i]->encode,
1997         MagickPathExtent);
1998     (void) ConcatenateMagickString(delegate,"        ",MagickPathExtent);
1999     delegate[8]='\0';
2000     commands=StringToList(delegate_info[i]->commands);
2001     if (commands == (char **) NULL)
2002       continue;
2003     (void) FormatLocaleFile(file,"%11s%c=%c%s  ",delegate_info[i]->decode ?
2004       delegate_info[i]->decode : "",delegate_info[i]->mode <= 0 ? '<' : ' ',
2005       delegate_info[i]->mode >= 0 ? '>' : ' ',delegate);
2006     StripString(commands[0]);
2007     (void) FormatLocaleFile(file,"\"%s\"\n",commands[0]);
2008     for (j=1; commands[j] != (char *) NULL; j++)
2009     {
2010       StripString(commands[j]);
2011       (void) FormatLocaleFile(file,"                     \"%s\"\n",commands[j]);
2012     }
2013     for (j=0; commands[j] != (char *) NULL; j++)
2014       commands[j]=DestroyString(commands[j]);
2015     commands=(char **) RelinquishMagickMemory(commands);
2016   }
2017   (void) fflush(file);
2018   delegate_info=(const DelegateInfo **)
2019     RelinquishMagickMemory((void *) delegate_info);
2020   return(MagickTrue);
2021 }
2022 
2023 /*
2024 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2025 %                                                                             %
2026 %                                                                             %
2027 %                                                                             %
2028 +   L o a d D e l e g a t e C a c h e                                         %
2029 %                                                                             %
2030 %                                                                             %
2031 %                                                                             %
2032 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2033 %
2034 %  LoadDelegateCache() loads the delegate configurations which provides a
2035 %  mapping between delegate attributes and a delegate name.
2036 %
2037 %  The format of the LoadDelegateCache method is:
2038 %
2039 %      MagickBooleanType LoadDelegateCache(LinkedListInfo *cache,
2040 %        const char *xml,const char *filename,const size_t depth,
2041 %        ExceptionInfo *exception)
2042 %
2043 %  A description of each parameter follows:
2044 %
2045 %    o xml:  The delegate list in XML format.
2046 %
2047 %    o filename:  The delegate list filename.
2048 %
2049 %    o depth: depth of <include /> statements.
2050 %
2051 %    o exception: return any errors or warnings in this structure.
2052 %
2053 */
LoadDelegateCache(LinkedListInfo * cache,const char * xml,const char * filename,const size_t depth,ExceptionInfo * exception)2054 static MagickBooleanType LoadDelegateCache(LinkedListInfo *cache,
2055   const char *xml,const char *filename,const size_t depth,
2056   ExceptionInfo *exception)
2057 {
2058   char
2059     keyword[MagickPathExtent],
2060     *token;
2061 
2062   const char
2063     *q;
2064 
2065   DelegateInfo
2066     *delegate_info;
2067 
2068   MagickStatusType
2069     status;
2070 
2071   size_t
2072     extent;
2073 
2074   /*
2075     Load the delegate map file.
2076   */
2077   (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
2078     "Loading delegate configuration file \"%s\" ...",filename);
2079   if (xml == (const char *) NULL)
2080     return(MagickFalse);
2081   status=MagickTrue;
2082   delegate_info=(DelegateInfo *) NULL;
2083   token=AcquireString(xml);
2084   extent=strlen(token)+MagickPathExtent;
2085   for (q=(const char *) xml; *q != '\0'; )
2086   {
2087     /*
2088       Interpret XML.
2089     */
2090     (void) GetNextToken(q,&q,extent,token);
2091     if (*token == '\0')
2092       break;
2093     (void) CopyMagickString(keyword,token,MagickPathExtent);
2094     if (LocaleNCompare(keyword,"<!DOCTYPE",9) == 0)
2095       {
2096         /*
2097           Doctype element.
2098         */
2099         while ((LocaleNCompare(q,"]>",2) != 0) && (*q != '\0'))
2100           (void) GetNextToken(q,&q,extent,token);
2101         continue;
2102       }
2103     if (LocaleNCompare(keyword,"<!--",4) == 0)
2104       {
2105         /*
2106           Comment element.
2107         */
2108         while ((LocaleNCompare(q,"->",2) != 0) && (*q != '\0'))
2109           (void) GetNextToken(q,&q,extent,token);
2110         continue;
2111       }
2112     if (LocaleCompare(keyword,"<include") == 0)
2113       {
2114         /*
2115           Include element.
2116         */
2117         while (((*token != '/') && (*(token+1) != '>')) && (*q != '\0'))
2118         {
2119           (void) CopyMagickString(keyword,token,MagickPathExtent);
2120           (void) GetNextToken(q,&q,extent,token);
2121           if (*token != '=')
2122             continue;
2123           (void) GetNextToken(q,&q,extent,token);
2124           if (LocaleCompare(keyword,"file") == 0)
2125             {
2126               if (depth > MagickMaxRecursionDepth)
2127                 (void) ThrowMagickException(exception,GetMagickModule(),
2128                   ConfigureError,"IncludeElementNestedTooDeeply","`%s'",token);
2129               else
2130                 {
2131                   char
2132                     path[MagickPathExtent],
2133                     *file_xml;
2134 
2135                   GetPathComponent(filename,HeadPath,path);
2136                   if (*path != '\0')
2137                     (void) ConcatenateMagickString(path,DirectorySeparator,
2138                       MagickPathExtent);
2139                   if (*token == *DirectorySeparator)
2140                     (void) CopyMagickString(path,token,MagickPathExtent);
2141                   else
2142                     (void) ConcatenateMagickString(path,token,MagickPathExtent);
2143                   file_xml=FileToXML(path,~0UL);
2144                   if (file_xml != (char *) NULL)
2145                     {
2146                       status&=LoadDelegateCache(cache,file_xml,path,
2147                         depth+1,exception);
2148                       file_xml=DestroyString(file_xml);
2149                     }
2150                 }
2151             }
2152         }
2153         continue;
2154       }
2155     if (LocaleCompare(keyword,"<delegate") == 0)
2156       {
2157         /*
2158           Delegate element.
2159         */
2160         delegate_info=(DelegateInfo *) AcquireCriticalMemory(
2161           sizeof(*delegate_info));
2162         (void) memset(delegate_info,0,sizeof(*delegate_info));
2163         delegate_info->path=ConstantString(filename);
2164         delegate_info->thread_support=MagickTrue;
2165         delegate_info->signature=MagickCoreSignature;
2166         continue;
2167       }
2168     if (delegate_info == (DelegateInfo *) NULL)
2169       continue;
2170     if ((LocaleCompare(keyword,"/>") == 0) ||
2171         (LocaleCompare(keyword,"</policy>") == 0))
2172       {
2173         status=AppendValueToLinkedList(cache,delegate_info);
2174         if (status == MagickFalse)
2175           (void) ThrowMagickException(exception,GetMagickModule(),
2176             ResourceLimitError,"MemoryAllocationFailed","`%s'",
2177             delegate_info->commands);
2178         delegate_info=(DelegateInfo *) NULL;
2179         continue;
2180       }
2181     (void) GetNextToken(q,(const char **) NULL,extent,token);
2182     if (*token != '=')
2183       continue;
2184     (void) GetNextToken(q,&q,extent,token);
2185     (void) GetNextToken(q,&q,extent,token);
2186     switch (*keyword)
2187     {
2188       case 'C':
2189       case 'c':
2190       {
2191         if (LocaleCompare((char *) keyword,"command") == 0)
2192           {
2193             char
2194               *commands;
2195 
2196             commands=AcquireString(token);
2197 #if defined(MAGICKCORE_WINDOWS_SUPPORT)
2198             if (strchr(commands,'@') != (char *) NULL)
2199               {
2200                 char
2201                   path[MagickPathExtent];
2202 
2203                 NTGhostscriptEXE(path,MagickPathExtent);
2204                 (void) SubstituteString((char **) &commands,"@PSDelegate@",
2205                   path);
2206                 (void) SubstituteString((char **) &commands,"\\","/");
2207               }
2208             (void) SubstituteString((char **) &commands,"&quot;","\"");
2209 #else
2210             (void) SubstituteString((char **) &commands,"&quot;","'");
2211 #endif
2212             (void) SubstituteString((char **) &commands,"&amp;","&");
2213             (void) SubstituteString((char **) &commands,"&gt;",">");
2214             (void) SubstituteString((char **) &commands,"&lt;","<");
2215             delegate_info->commands=commands;
2216             break;
2217           }
2218         break;
2219       }
2220       case 'D':
2221       case 'd':
2222       {
2223         if (LocaleCompare((char *) keyword,"decode") == 0)
2224           {
2225             delegate_info->decode=ConstantString(token);
2226             delegate_info->mode=1;
2227             break;
2228           }
2229         break;
2230       }
2231       case 'E':
2232       case 'e':
2233       {
2234         if (LocaleCompare((char *) keyword,"encode") == 0)
2235           {
2236             delegate_info->encode=ConstantString(token);
2237             delegate_info->mode=(-1);
2238             break;
2239           }
2240         break;
2241       }
2242       case 'M':
2243       case 'm':
2244       {
2245         if (LocaleCompare((char *) keyword,"mode") == 0)
2246           {
2247             delegate_info->mode=1;
2248             if (LocaleCompare(token,"bi") == 0)
2249               delegate_info->mode=0;
2250             else
2251               if (LocaleCompare(token,"encode") == 0)
2252                 delegate_info->mode=(-1);
2253             break;
2254           }
2255         break;
2256       }
2257       case 'S':
2258       case 's':
2259       {
2260         if (LocaleCompare((char *) keyword,"spawn") == 0)
2261           {
2262             delegate_info->spawn=IsStringTrue(token);
2263             break;
2264           }
2265         if (LocaleCompare((char *) keyword,"stealth") == 0)
2266           {
2267             delegate_info->stealth=IsStringTrue(token);
2268             break;
2269           }
2270         break;
2271       }
2272       case 'T':
2273       case 't':
2274       {
2275         if (LocaleCompare((char *) keyword,"thread-support") == 0)
2276           {
2277             delegate_info->thread_support=IsStringTrue(token);
2278             if (delegate_info->thread_support == MagickFalse)
2279               delegate_info->semaphore=AcquireSemaphoreInfo();
2280             break;
2281           }
2282         break;
2283       }
2284       default:
2285         break;
2286     }
2287   }
2288   token=(char *) RelinquishMagickMemory(token);
2289   return(status != 0 ? MagickTrue : MagickFalse);
2290 }
2291