]> granicus.if.org Git - imagemagick/blob - MagickWand/magick-cli.c
(no commit message)
[imagemagick] / MagickWand / magick-cli.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %                 M   M   AAA    GGGG  IIIII   CCCC  K   K                    %
7 %                 MM MM  A   A  G        I    C      K  K                     %
8 %                 M M M  AAAAA  G GGG    I    C      KKK                      %
9 %                 M   M  A   A  G   G    I    C      K  K                     %
10 %                 M   M  A   A   GGGG  IIIII   CCCC  K   K                    %
11 %                                                                             %
12 %                            CCCC  L      IIIII                               %
13 %                           C      L        I                                 %
14 %                           C      L        I                                 %
15 %                           C      L        I                                 %
16 %                            CCCC  LLLLL  IIIII                               %
17 %                                                                             %
18 %       Perform "Magick" on Images via the Command Line Interface             %
19 %                                                                             %
20 %                             Dragon Computing                                %
21 %                             Anthony Thyssen                                 %
22 %                               January 2012                                  %
23 %                                                                             %
24 %                                                                             %
25 %  Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization      %
26 %  dedicated to making software imaging solutions freely available.           %
27 %                                                                             %
28 %  You may not use this file except in compliance with the License.  You may  %
29 %  obtain a copy of the License at                                            %
30 %                                                                             %
31 %    http://www.imagemagick.org/script/license.php                            %
32 %                                                                             %
33 %  Unless required by applicable law or agreed to in writing, software        %
34 %  distributed under the License is distributed on an "AS IS" BASIS,          %
35 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
36 %  See the License for the specific language governing permissions and        %
37 %  limitations under the License.                                             %
38 %                                                                             %
39 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
40 %
41 %  Read CLI arguments, script files, and pipelines, to provide options that
42 %  manipulate images from many different formats.
43 %
44 */
45 \f
46 /*
47   Include declarations.
48 */
49 #include "MagickWand/studio.h"
50 #include "MagickWand/MagickWand.h"
51 #include "MagickWand/magick-wand-private.h"
52 #include "MagickWand/wandcli.h"
53 #include "MagickWand/wandcli-private.h"
54 #include "MagickWand/operation.h"
55 #include "MagickWand/magick-cli.h"
56 #include "MagickWand/script-token.h"
57 #include "MagickCore/utility-private.h"
58 #include "MagickCore/version.h"
59 \f
60 /* verbose debugging,
61       3 - option type details
62       9 - output options/artifacts/propertys
63 */
64 #define MagickCommandDebug 0
65
66 #define ThrowFileException(exception,severity,tag,context) \
67 { \
68   char \
69     *message; \
70  \
71   message=GetExceptionMessage(errno); \
72   (void) ThrowMagickException(exception,GetMagickModule(),severity, \
73     tag == (const char *) NULL ? "unknown" : tag,"'%s': %s",context,message); \
74   message=DestroyString(message); \
75 }
76
77 #if MagickCommandDebug >= 9
78 /*
79   Temporary Debugging Information
80   FUTURE: these should be able to be printed out using 'percent escapes'
81   Actually 'Properities' can already be output with  "%[*]"
82 */
83 static void OutputOptions(ImageInfo *image_info)
84 {
85   const char
86     *option,
87     *value;
88
89   (void) FormatLocaleFile(stdout,"  Global Options:\n");
90   ResetImageOptionIterator(image_info);
91   while ((option=GetNextImageOption(image_info)) != (const char *) NULL ) {
92     (void) FormatLocaleFile(stdout,"    %s: ",option);
93     value=GetImageOption(image_info,option);
94     if (value != (const char *) NULL)
95       (void) FormatLocaleFile(stdout,"%s\n",value);
96   }
97   ResetImageOptionIterator(image_info); 
98 }
99
100 static void OutputArtifacts(Image *image)
101 {
102   const char
103     *artifact,
104     *value;
105
106   (void) FormatLocaleFile(stdout,"  Image Artifacts:\n");
107   ResetImageArtifactIterator(image);
108   while ((artifact=GetNextImageArtifact(image)) != (const char *) NULL ) {
109     (void) FormatLocaleFile(stdout,"    %s: ",artifact);
110     value=GetImageArtifact(image,artifact);
111     if (value != (const char *) NULL)
112       (void) FormatLocaleFile(stdout,"%s\n",value);
113   }
114   ResetImageArtifactIterator(image);
115 }
116
117 static void OutputProperties(Image *image,ExceptionInfo *exception)
118 {
119   const char
120     *property,
121     *value;
122
123   (void) FormatLocaleFile(stdout,"  Image Properity:\n");
124   ResetImagePropertyIterator(image);
125   while ((property=GetNextImageProperty(image)) != (const char *) NULL ) {
126     (void) FormatLocaleFile(stdout,"    %s: ",property);
127     value=GetImageProperty(image,property,exception);
128     if (value != (const char *) NULL)
129       (void) FormatLocaleFile(stdout,"%s\n",value);
130   }
131   ResetImagePropertyIterator(image);
132 }
133 #endif
134
135 \f
136 /*
137 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
138 %                                                                             %
139 %                                                                             %
140 %                                                                             %
141 +   P r o c e s s S c r i p t O p t i o n s                                   %
142 %                                                                             %
143 %                                                                             %
144 %                                                                             %
145 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
146 %
147 %  ProcessScriptOptions() reads options and processes options as they are
148 %  found in the given file, or pipeline.  The filename to open and read
149 %  options is given as the 'index' argument of the argument array given.
150 %
151 %  Other arguments following index may be read by special script options
152 %  as settings (strings), images, or as operations to be processed in various
153 %  ways.   How they are treated is up to the script being processed.
154 %
155 %  Note that a script not 'return' to the command line processing, nor can
156 %  they call (and return from) other scripts. At least not at this time.
157 %
158 %  There are no 'ProcessOptionFlags' control flags at this time.
159 %
160 %  The format of the ProcessScriptOptions method is:
161 %
162 %    void ProcessScriptOptions(MagickCLI *cli_wand,int argc,char **argv,
163 %               int index)
164 %
165 %  A description of each parameter follows:
166 %
167 %    o cli_wand: the main CLI Wand to use.
168 %
169 %    o argc: the number of elements in the argument vector.
170 %
171 %    o argv: A text array containing the command line arguments.
172 %
173 %    o index: offset for argc to CLI argumnet count
174 %
175 */
176 WandExport void ProcessScriptOptions(MagickCLI *cli_wand,int argc,char **argv,
177      int index)
178 {
179   ScriptTokenInfo
180     *token_info;
181
182   CommandOptionFlags
183     option_type;
184
185   int
186     count;
187
188   char
189     *option,
190     *arg1,
191     *arg2;
192
193   assert(argc>index); /* at least one argument - script name */
194   assert(argv != (char **)NULL);
195   assert(argv[index] != (char *)NULL);
196   assert(argv[argc-1] != (char *)NULL);
197   assert(cli_wand != (MagickCLI *) NULL);
198   assert(cli_wand->signature == WandSignature);
199   if (cli_wand->wand.debug != MagickFalse)
200     (void) LogMagickEvent(WandEvent,GetMagickModule(),"%s",cli_wand->wand.name);
201
202   /* open file script or stream, and set up tokenizer */
203   token_info = AcquireScriptTokenInfo(argv[index]);
204   if (token_info == (ScriptTokenInfo *) NULL) {
205     ThrowFileException(cli_wand->wand.exception,OptionFatalError,
206                "UnableToOpenScript",argv[index]);
207     return;
208   }
209
210   /* define the error location string for use in exceptions
211      order of input escapes: option, (arg), filename, line, column */
212   cli_wand->location="'%s' in \"%s\" line %u column %u";
213   cli_wand->location2="'%s' '%s' in \"%s\" line %u column %u";
214   if ( LocaleCompare("-", argv[index]) == 0 )
215     cli_wand->filename="stdin";
216   else
217     cli_wand->filename=argv[index];
218
219   /* Process Options from Script */
220   option = arg1 = arg2 = (char*)NULL;
221   while (1) {
222
223     /* Get a option */
224     { MagickBooleanType status = GetScriptToken(token_info);
225       cli_wand->line=token_info->token_line;
226       cli_wand->column=token_info->token_column;
227       if( IfMagickFalse(status) )
228         break; /* error or end of options */
229     }
230
231     /* save option details */
232     CloneString(&option,token_info->token);
233
234     { /* get option type and argument count */
235       const OptionInfo *option_info = GetCommandOptionInfo(option);
236       count=option_info->type;
237       option_type=(CommandOptionFlags) option_info->flags;
238 #if 0
239       (void) FormatLocaleFile(stderr, "Script: %u,%u: \"%s\" matched \"%s\"\n",
240              cli_wand->line, cli_wand->line, option, option_info->mnemonic );
241 #endif
242     }
243
244     /* handle a undefined option - image read? */
245     if ( option_type == UndefinedOptionFlag ||
246          (option_type & NonMagickOptionFlag) != 0 ) {
247 #if MagickCommandDebug >= 3
248       (void) FormatLocaleFile(stderr, "Script %u,%u Non-Option: \"%s\"\n",
249                   cli_wand->line, cli_wand->line, option);
250 #endif
251       if ( IfMagickFalse(IsCommandOption(option))) {
252         /* non-option -- treat as a image read */
253         CLISpecialOperator(cli_wand,"-read",option);
254         goto next_token;
255       }
256       if ( LocaleCompare(option,"-script") == 0 ) {
257         option_type=SpecialOptionFlag;
258         count=1;
259         /* fall thru - collect one argument */
260       }
261       else {
262         CLIWandExceptionBreak(OptionFatalError,"UnrecognizedOption",option);
263         goto next_token;
264       }
265     }
266
267     if ( count >= 1 ) {
268       if( IfMagickFalse(GetScriptToken(token_info)) )
269         CLIWandException(OptionFatalError,"MissingArgument",option);
270       CloneString(&arg1,token_info->token);
271     }
272     else
273       CloneString(&arg1,(char *)NULL);
274
275     if ( count >= 2 ) {
276       if( IfMagickFalse(GetScriptToken(token_info)) )
277         CLIWandExceptionBreak(OptionFatalError,"MissingArgument",option);
278       CloneString(&arg2,token_info->token);
279     }
280     else
281       CloneString(&arg2,(char *)NULL);
282
283 #if MagickCommandDebug >= 3
284     (void) FormatLocaleFile(stderr,
285       "Script %u,%u Option: \"%s\"  Count: %d  Flags: %04x  Args: \"%s\" \"%s\"\n",
286           cli_wand->line,cli_wand->line,option,count,option_type,arg1,arg2);
287 #endif
288
289     if ( (option_type & GenesisOptionFlag) != 0 ) {
290       /* Genesis Options have no place in a magick script */
291       CLIWandExceptionBreak(OptionError,"InvalidUseOfOption",option);
292       goto next_token;
293     }
294     if ( (option_type & DeprecateOptionFlag) != 0 ) {
295       CLIWandException(OptionWarning,"DeprecatedOption",option);
296       if ( CLICatchException(cli_wand, MagickFalse) != MagickFalse )
297         break;
298       /* fall through - do the depreciated option */
299     }
300     if (((option_type & ImageRequiredFlags) != 0 ) &&
301         ( cli_wand->wand.images == (Image *)NULL ) ) {
302       CLIWandException(OptionError,"NoImagesFound",option);
303       goto next_token;
304     }
305
306     /* handle special script-argument options here */
307     //either continue processing command line
308     // or making use of the command line options.
309     //CLICommandOptions(cli_wand,count+1,argv, MagickScriptArgsFlags);
310
311     /*
312       Process Option from file
313     */
314     if ( (option_type & SpecialOptionFlag) != 0 ) {
315       if ( LocaleCompare(option,"-exit") == 0 ) {
316         break; /* forced end of script */
317       }
318       else if ( LocaleCompare(option,"-script") == 0 ) {
319         /* FUTURE: call new script from this script */
320         CLIWandExceptionBreak(OptionError,"InvalidUseOfOption",option);
321         goto next_token;
322       }
323       /* handle any other special operators now */
324       CLISpecialOperator(cli_wand,option,arg1);
325     }
326
327     if ( (option_type & SettingOptionFlags) != 0 ) {
328       CLISettingOptionInfo(cli_wand, option, arg1, arg2);
329       // FUTURE: Sync Specific Settings into Image Properities (not global)
330     }
331     if ( cli_wand->wand.images != (Image *)NULL )
332       SyncImagesSettings(cli_wand->wand.image_info,cli_wand->wand.images,
333            cli_wand->wand.exception);
334
335     if ( (option_type & SimpleOperatorOptionFlag) != 0)
336       CLISimpleOperatorImages(cli_wand, option, arg1, arg2);
337
338     if ( (option_type & ListOperatorOptionFlag) != 0 )
339       CLIListOperatorImages(cli_wand, option, arg1, arg2);
340
341 next_token:
342 #if MagickCommandDebug >= 9
343     OutputOptions(cli_wand->wand.image_info);
344     if ( cli_wand->wand.images != (Image *)NULL ) {
345       OutputArtifacts(cli_wand->wand.images);
346       OutputProperties(cli_wand->wand.images,cli_wand->wand.exception);
347     }
348 #endif
349     if ( CLICatchException(cli_wand, MagickFalse) != MagickFalse )
350       break;
351   }
352
353 #if MagickCommandDebug >= 3
354   (void) FormatLocaleFile(stderr, "Script End: %d\n", token_info->status);
355 #endif
356   switch( token_info->status ) {
357     case TokenStatusOK:
358     case TokenStatusEOF:
359       if (cli_wand->image_list_stack != (Stack *)NULL)
360         CLIWandException(OptionError,"UnbalancedParenthesis", "(eof)");
361       else if (cli_wand->image_info_stack != (Stack *)NULL)
362         CLIWandException(OptionError,"UnbalancedBraces", "(eof)");
363       break;
364     case TokenStatusBadQuotes:
365       /* Ensure last token has a sane length for error report */
366       if( strlen(token_info->token) > INITAL_TOKEN_LENGTH-1 ) {
367         token_info->token[INITAL_TOKEN_LENGTH-4] = '.';
368         token_info->token[INITAL_TOKEN_LENGTH-3] = '.';
369         token_info->token[INITAL_TOKEN_LENGTH-2] = '.';
370         token_info->token[INITAL_TOKEN_LENGTH-1] = '\0';
371       }
372       CLIWandException(OptionFatalError,"ScriptUnbalancedQuotes",
373            token_info->token);
374       break;
375     case TokenStatusMemoryFailed:
376       CLIWandException(OptionFatalError,"ScriptTokenMemoryFailed","");
377       break;
378     case TokenStatusBinary:
379       CLIWandException(OptionFatalError,"ScriptIsBinary","");
380       break;
381   }
382
383   /* Clean up */
384   token_info = DestroyScriptTokenInfo(token_info);
385
386   CloneString(&option,(char *)NULL);
387   CloneString(&arg1,(char *)NULL);
388   CloneString(&arg2,(char *)NULL);
389
390   return;
391 }
392 \f
393 /*
394 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
395 %                                                                             %
396 %                                                                             %
397 %                                                                             %
398 +  P r o c e s s C o m m a n d O p t i o n s                                  %
399 %                                                                             %
400 %                                                                             %
401 %                                                                             %
402 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
403 %
404 %  ProcessCommandOptions() reads and processes arguments in the given
405 %  command line argument array. The array does not contain the command
406 %  being processed, only the options.
407 %
408 %  The 'process_flags' can be used to control and limit option processing.
409 %  For example, to only process one option, or how unknown and special options
410 %  are to be handled, and if the last argument in array is to be regarded as a
411 %  final image write argument (filename or special coder).
412 %
413 %  The format of the ProcessCommandOptions method is:
414 %
415 %    int ProcessCommandOptions(MagickCLI *cli_wand,int argc,char **argv,
416 %           int index, ProcessOptionFlags process_flags )
417 %
418 %  A description of each parameter follows:
419 %
420 %    o cli_wand: the main CLI Wand to use.
421 %
422 %    o argc: the number of elements in the argument vector.
423 %
424 %    o argv: A text array containing the command line arguments.
425 %
426 %    o process_flags: What type of arguments will be processed, ignored
427 %                     or return errors.
428 %
429 %    o index: index in the argv array to start processing from
430 %
431 % The function returns the index ot the next option to be processed. This
432 % is really only releven if process_flags contains a ProcessOneOptionOnly
433 % flag.
434 %
435 */
436 WandExport int ProcessCommandOptions(MagickCLI *cli_wand, int argc,
437      char **argv, int index, ProcessOptionFlags process_flags )
438 {
439   const char
440     *option,
441     *arg1,
442     *arg2;
443
444   int
445     i,
446     end,
447     count;
448
449   CommandOptionFlags
450     option_type;
451
452   assert(argc>=index); /* you may have no arguments left! */
453   assert(argv != (char **)NULL);
454   assert(argv[index] != (char *)NULL);
455   assert(argv[argc-1] != (char *)NULL);
456   assert(cli_wand != (MagickCLI *) NULL);
457   assert(cli_wand->signature == WandSignature);
458   if (cli_wand->wand.debug != MagickFalse)
459     (void) LogMagickEvent(WandEvent,GetMagickModule(),"%s",cli_wand->wand.name);
460
461   /* define the error location string for use in exceptions
462      order of input escapes: option, (arg), filename, line, column */
463   cli_wand->location="'%s' %s arg %u";
464   cli_wand->location2="'%s' '%s' %s arg %u";
465   cli_wand->filename="CLI";
466
467   end = argc;
468   if ( ( process_flags & ProcessOutputFile ) != 0 )
469     end--;
470
471   for (i=index; i < end; i += count +1) {
472     /* Finished processing one option? */
473     if ( ( process_flags & ProcessOneOptionOnly ) != 0 && i != index )
474       return(i);
475
476     option=argv[i];
477     cli_wand->line=i;
478
479     { const OptionInfo *option_info = GetCommandOptionInfo(argv[i]);
480       count=option_info->type;
481       option_type=(CommandOptionFlags) option_info->flags;
482 #if 0
483       (void) FormatLocaleFile(stderr, "CLI %d: \"%s\" matched \"%s\"\n",
484             i, argv[i], option_info->mnemonic );
485 #endif
486     }
487
488     if ( option_type == UndefinedOptionFlag ||
489          (option_type & NonMagickOptionFlag) != 0 ) {
490 #if MagickCommandDebug >= 3
491       (void) FormatLocaleFile(stderr, "CLI %d Non-Option: \"%s\"\n", i, option);
492 #endif
493       if ( IfMagickFalse(IsCommandOption(option)) ) {
494          if ( (process_flags & ProcessNonOptionImageRead) != 0 )
495            /* non-option -- treat as a image read */
496            CLISpecialOperator(cli_wand,"-read",option);
497          else
498            CLIWandException(OptionFatalError,"UnrecognizedOption",option);
499          goto next_argument;
500       }
501       if ( ((process_flags & ProcessScriptOption) != 0) &&
502            (LocaleCompare(option,"-script") == 0) ) {
503         /* Call Script from CLI, with a filename as a zeroth argument.
504            NOTE: -script may need to use 'implict write filename' so it
505            must be handled here to prevent 'missing argument' error.
506         */
507         ProcessScriptOptions(cli_wand,argc,argv,i+1);
508         return(argc);  /* Script does not return to CLI -- Yet -- FUTURE */
509       }
510       CLIWandException(OptionFatalError,"UnrecognizedOption",option);
511       goto next_argument;
512     }
513
514     if ((i+count) >= end ) {
515       CLIWandException(OptionFatalError,"MissingArgument",option);
516       if ( CLICatchException(cli_wand, MagickFalse) != MagickFalse )
517         return(end);
518       goto next_argument; /* no more arguments unable to proceed */
519     }
520
521     arg1 = ( count >= 1 ) ? argv[i+1] : (char *)NULL;
522     arg2 = ( count >= 2 ) ? argv[i+2] : (char *)NULL;
523
524 #if MagickCommandDebug >= 3
525     (void) FormatLocaleFile(stderr,
526       "CLI %u Option: \"%s\"  Count: %d  Flags: %04x  Args: \"%s\" \"%s\"\n",
527           i,option,count,option_type,arg1,arg2);
528 #endif
529
530     if ( (option_type & DeprecateOptionFlag) != 0 ) {
531       CLIWandException(OptionWarning,"DeprecatedOption",option);
532       if ( CLICatchException(cli_wand, MagickFalse) != MagickFalse )
533         return(end);
534       /* fall through - do the depreciated option */
535     }
536     if ( (option_type & GenesisOptionFlag) != 0 ) {
537       goto next_argument;  /* ignore genesis options */
538     }
539     if (((option_type & ImageRequiredFlags) != 0 ) &&
540         ( cli_wand->wand.images == (Image *)NULL ) ) {
541       CLIWandException(OptionError,"NoImagesFound",option);
542       goto next_argument;
543     }
544
545     if ( (option_type & SpecialOptionFlag) != 0 ) {
546       if ( ( process_flags & ProcessExitOption ) != 0
547            && LocaleCompare(option,"-exit") == 0 )
548         return(i+count);
549       /* handle any other special operators now */
550       CLISpecialOperator(cli_wand,option,arg1);
551     }
552
553     if ( (option_type & SettingOptionFlags) != 0 ) {
554       CLISettingOptionInfo(cli_wand, option, arg1, arg2);
555       // FUTURE: Sync individual Settings into images (no SyncImageSettings())
556     }
557     if ( cli_wand->wand.images != (Image *)NULL )
558       SyncImagesSettings(cli_wand->wand.image_info,cli_wand->wand.images,
559           cli_wand->wand.exception);
560
561     if ( (option_type & SimpleOperatorOptionFlag) != 0)
562       CLISimpleOperatorImages(cli_wand, option, arg1, arg2);
563
564     if ( (option_type & ListOperatorOptionFlag) != 0 )
565       CLIListOperatorImages(cli_wand, option, arg1, arg2);
566
567 next_argument:
568 #if MagickCommandDebug >= 9
569     OutputOptions(cli_wand->wand.image_info);
570     if ( cli_wand->wand.images != (Image *)NULL ) {
571       OutputArtifacts(cli_wand->wand.images);
572       OutputProperties(cli_wand->wand.images,cli_wand->wand.exception);
573     }
574 #endif
575     if ( CLICatchException(cli_wand, MagickFalse) != MagickFalse )
576       return(i+count);
577   }
578   assert(i==end);
579
580   if ( ( process_flags & ProcessOutputFile ) == 0 )
581     return(end);
582
583   assert(end==argc-1);
584
585   /*
586      Implicit Write of images to final CLI argument
587   */
588   option=argv[i];
589   cli_wand->line=i;
590
591 #if MagickCommandDebug >= 3
592   (void) FormatLocaleFile(stderr, "CLI %d Write File: \"%s\"\n", i, option );
593 #endif
594
595   /* check that stacks are empty */
596   if (cli_wand->image_list_stack != (Stack *)NULL)
597     CLIWandException(OptionError,"UnbalancedParenthesis", "(eof)");
598   else if (cli_wand->image_info_stack != (Stack *)NULL)
599     CLIWandException(OptionError,"UnbalancedBraces", "(eof)");
600   if ( CLICatchException(cli_wand, MagickFalse) != MagickFalse )
601     return(argc);
602
603   /* This is a valid 'do no write' option - no images needed */
604   if (LocaleCompare(option,"-exit") == 0 )
605     return(argc);  /* just exit, no image write */
606
607   /* If filename looks like an option -- produce an error */
608   if (IsCommandOption(option) != MagickFalse) {
609     CLIWandException(OptionError,"MissingOutputFilename",option);
610     return(argc);
611   }
612
613   CLISpecialOperator(cli_wand,"-write",option);
614   return(argc);
615 }
616 \f
617 /*
618 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
619 %                                                                             %
620 %                                                                             %
621 %                                                                             %
622 +   M a g i c k I m a g e C o m m a n d                                       %
623 %                                                                             %
624 %                                                                             %
625 %                                                                             %
626 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
627 %
628 %  MagickImageCommand() Handle special use CLI arguments and prepare a
629 %  CLI MagickCLI to process the command line or directly specified script.
630 %
631 %  This is essentualy interface function between the MagickCore library
632 %  initialization function MagickCommandGenesis(), and the option MagickCLI
633 %  processing functions  ProcessCommandOptions()  or  ProcessScriptOptions()
634 %
635 %  The format of the MagickImageCommand method is:
636 %
637 %      MagickBooleanType MagickImageCommand(ImageInfo *image_info,
638 %           int argc, char **argv, char **metadata, ExceptionInfo *exception)
639 %
640 %  A description of each parameter follows:
641 %
642 %    o image_info: the starting image_info structure
643 %         (for compatibilty with MagickCommandGenisis())
644 %
645 %    o argc: the number of elements in the argument vector.
646 %
647 %    o argv: A text array containing the command line arguments.
648 %
649 %    o metadata: any metadata (for VBS) is returned here.
650 %         (for compatibilty with MagickCommandGenisis())
651 %
652 %    o exception: return any errors or warnings in this structure.
653 %
654 */
655
656 static void MagickUsage(MagickBooleanType verbose)
657 {
658   const char
659     *name;
660
661   size_t
662     len;
663
664   name=GetClientName();
665   len=strlen(name);
666
667   if (len>=6 && LocaleCompare("script",name+len-6) == 0) {
668     /* magick-script usage */
669     (void) FormatLocaleFile(stdout,
670        "Usage: %s {filename} [{script_args}...]\n",name);
671   }
672   else if (len>=7 && LocaleCompare("convert",name+len-7) == 0) {
673     /* convert usage */
674     (void) FormatLocaleFile(stdout,
675        "Usage: %s [{option}|{image}...] {output_image}\n",name);
676     (void) FormatLocaleFile(stdout,
677        "       %s -help|-version|-usage|-list {option}\n",name);
678   }
679   else {
680     /* magick usage */
681     (void) FormatLocaleFile(stdout,
682        "Usage: %s [{option}|{image}...] {output_image}\n",name);
683     (void) FormatLocaleFile(stdout,
684        "       %s [{option}|{image}...] -script {filename} [{script_args}...]\n",
685        name);
686     (void) FormatLocaleFile(stdout,
687        "       %s -help|-version|-usage|-list {option}\n",name);
688   }
689
690   if (IfMagickFalse(verbose))
691     return;
692
693   (void) FormatLocaleFile(stdout,"\n%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
694     "All options are performed in a strict 'as you see them' order\n",
695     "You must read-in images before you can operate on them.\n",
696     "\n",
697     "Magick Script files can use any of the following forms...\n",
698     "     #!/path/to/magick -script\n",
699     "or\n",
700     "     #!/bin/sh\n",
701     "     :; exec magick -script \"$0\" \"$@\"; exit 10\n",
702     "     # Magick script from here...\n",
703     "or\n",
704     "     #!/usr/bin/env  magick-script\n",
705     "The latter two forms do not require the path to the command hard coded.\n",
706     "Note: \"magick-script\" needs to be linked to the \"magick\" command.\n",
707     "\n",
708     "For more information on usage, options, examples, and techniques\n",
709     "see the ImageMagick website at    ", MagickAuthoritativeURL);
710
711   return;
712 }
713
714 /*
715    Concatanate given file arguments to the given output argument.
716    Used for a special -concatenate option used for specific 'delegates'.
717    The option is not formally documented.
718
719       magick -concatenate files... output
720
721    This is much like the UNIX "cat" command, but for both UNIX and Windows,
722    however the last argument provides the output filename.
723 */
724 static MagickBooleanType ConcatenateImages(int argc,char **argv,
725   ExceptionInfo *exception)
726 {
727   FILE
728     *input,
729     *output;
730
731   int
732     c;
733
734   register ssize_t
735     i;
736
737   output=fopen_utf8(argv[argc-1],"wb");
738   if (output == (FILE *) NULL) {
739     ThrowFileException(exception,FileOpenError,"UnableToOpenFile",
740       argv[argc-1]);
741     return(MagickFalse);
742   }
743   for (i=2; i < (ssize_t) (argc-1); i++) {
744     input=fopen_utf8(argv[i],"rb");
745     if (input == (FILE *) NULL)
746       ThrowFileException(exception,FileOpenError,"UnableToOpenFile",argv[i]);
747     for (c=fgetc(input); c != EOF; c=fgetc(input))
748       (void) fputc((char) c,output);
749     (void) fclose(input);
750     (void) remove_utf8(argv[i]);
751   }
752   (void) fclose(output);
753   return(MagickTrue);
754 }
755
756 WandExport MagickBooleanType MagickImageCommand(ImageInfo *image_info,
757   int argc,char **argv,char **metadata,ExceptionInfo *exception)
758 {
759   MagickCLI
760     *cli_wand;
761
762   const char
763     *option;
764
765   size_t
766     len;
767
768   ProcessOptionFlags
769     process_flags = MagickCommandOptionFlags;
770
771   /* For specific OS command line requirements */
772   ReadCommandlLine(argc,&argv);
773
774 #if 0
775   status=ExpandFilenames(&argc,&argv);
776   if ( IfMagickFalse(status) )
777     ThrowConvertException(ResourceLimitError,"MemoryAllocationFailed",
778       GetExceptionMessage(errno));
779 #endif
780
781   /* Initialize special "CLI Wand" to hold images and settings (empty) */
782   cli_wand=AcquireMagickCLI(image_info,exception);
783   cli_wand->line=1;
784
785   GetPathComponent(argv[0],TailPath,cli_wand->wand.name);
786   SetClientName(cli_wand->wand.name);
787   ConcatenateMagickString(cli_wand->wand.name,"-CLI",MaxTextExtent);
788
789   len=strlen(argv[0]);  /* precaution */
790
791   /* "convert" command - give a "depreciation" warning" */
792   if (len>=7 && LocaleCompare("convert",argv[0]+len-7) == 0) {
793     process_flags = ConvertCommandOptionFlags;
794     /*(void) FormatLocaleFile(stderr,"WARNING: %s\n",
795              "The convert is depreciated in IMv7, use \"magick\"\n");*/
796   }
797
798   /* Special Case:  If command name ends with "script" implied "-script" */
799   if (len>=6 && LocaleCompare("script",argv[0]+len-6) == 0) {
800     if (argc >= 2 && *(argv[1]) != '-') {
801       GetPathComponent(argv[1],TailPath,cli_wand->wand.name);
802       ProcessScriptOptions(cli_wand,argc,argv,1);
803       goto Magick_Command_Cleanup;
804     }
805   }
806
807   /* Special Case: Version Information and Abort */
808   if (argc == 2) {
809     option=argv[1];
810     if (LocaleCompare("-version",option) == 0) {
811       CLISpecialOperator(cli_wand, "-version", (char *)NULL);
812       goto Magick_Command_Exit;
813     }
814     if ((LocaleCompare("-help",option) == 0)   || /* GNU standard option */
815         (LocaleCompare("--help",option) == 0) ) {
816       MagickUsage(MagickFalse);
817       goto Magick_Command_Exit;
818     }
819     if (LocaleCompare("-usage",option) == 0) {
820       CLISpecialOperator(cli_wand, "-version", (char *)NULL);
821       MagickUsage(MagickTrue);
822       goto Magick_Command_Exit;
823     }
824   }
825
826   /* not enough arguments -- including -help */
827   if (argc < 3) {
828     (void) FormatLocaleFile(stderr,
829        "Error: Invalid argument or not enough arguments\n\n");
830     MagickUsage(MagickFalse);
831     goto Magick_Command_Exit;
832   }
833
834   /* List Information and Abort */
835   if (LocaleCompare("-list",argv[1]) == 0) {
836     CLISpecialOperator(cli_wand, argv[1], argv[2]);
837     goto Magick_Command_Exit;
838   }
839
840   /* Special "concatenate option (hidden) for delegate usage */
841   if (LocaleCompare("-concatenate",argv[1]) == 0) {
842     ConcatenateImages(argc,argv,exception);
843     goto Magick_Command_Exit;
844   }
845
846   /* ------------- */
847   /* The Main Call */
848
849   if (LocaleCompare("-script",argv[1]) == 0) {
850     /* Start processing directly from script, no pre-script options
851        Replace wand command name with script name
852        First argument in the argv array is the script name to read.
853     */
854     GetPathComponent(argv[2],TailPath,cli_wand->wand.name);
855     ProcessScriptOptions(cli_wand,argc,argv,2);
856   }
857   else {
858     /* Normal Command Line, assumes output file as last option */
859     ProcessCommandOptions(cli_wand,argc,argv,1, process_flags);
860   }
861   /* ------------- */
862
863 Magick_Command_Cleanup:
864   /* recover original image_info and clean up stacks */
865   while (cli_wand->image_list_stack != (Stack *)NULL)
866     CLISpecialOperator(cli_wand,")",(const char *)NULL);
867   while (cli_wand->image_info_stack != (Stack *)NULL)
868     CLISpecialOperator(cli_wand,"}",(const char *)NULL);
869
870   /* assert we have recovered the original structures */
871   assert(cli_wand->wand.image_info == image_info);
872   assert(cli_wand->wand.exception == exception);
873
874   /* Handle metadata for ImageMagickObject COM object for Windows VBS */
875   if (metadata != (char **) NULL) {
876     const char
877       *format;
878
879     char
880       *text;
881
882     format="%w,%h,%m";   // Get this from image_info Option splaytree
883
884     text=InterpretImageProperties(image_info,cli_wand->wand.images,format,
885          exception);
886     if (text == (char *) NULL)
887       ThrowMagickException(exception,GetMagickModule(),ResourceLimitError,
888            "MemoryAllocationFailed","`%s'", GetExceptionMessage(errno));
889     else {
890       (void) ConcatenateString(&(*metadata),text);
891       text=DestroyString(text);
892     }
893   }
894
895 Magick_Command_Exit:
896   /* Destroy the special CLI Wand */
897   cli_wand->wand.image_info = (ImageInfo *)NULL; /* not these */
898   cli_wand->wand.exception = (ExceptionInfo *)NULL;
899   cli_wand=DestroyMagickCLI(cli_wand);
900
901   return(IsMagickTrue(exception->severity > ErrorException));
902 }