magick-cli.c revision 4cb3726620f19a7f6e00a5f96fa9b8a81d83d5c6
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
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/operation.h"
53#include "MagickWand/operation-private.h"
54#include "MagickWand/magick-cli.h"
55#include "MagickWand/script-token.h"
56#include "MagickCore/utility-private.h"
57#include "MagickCore/version.h"
58
59/* verbose debugging,
60      1 - option type
61      2 - source of option
62      3 - mnemonic lookup
63      4 - output options/artifacts
64*/
65#define MagickCommandDebug 0
66
67#define ThrowFileException(exception,severity,tag,context) \
68{ \
69  char \
70    *message; \
71 \
72  message=GetExceptionMessage(errno); \
73  (void) ThrowMagickException(exception,GetMagickModule(),severity, \
74    tag == (const char *) NULL ? "unknown" : tag,"`%s': %s",context,message); \
75  message=DestroyString(message); \
76}
77
78#if MagickCommandDebug >= 4
79static void OutputOptions(ImageInfo *image_info)
80{
81  const char
82    *option,
83    *value;
84
85  (void) FormatLocaleFile(stdout,"  Image_Info Options:\n");
86  ResetImageOptionIterator(image_info);
87  while ((option=GetNextImageOption(image_info)) != (const char *) NULL ) {
88    (void) FormatLocaleFile(stdout,"    %s: ",option);
89    value=GetImageOption(image_info,option);
90    if (value != (const char *) NULL)
91      (void) FormatLocaleFile(stdout,"%s\n",value);
92  }
93  ResetImageOptionIterator(image_info);
94}
95
96static void OutputArtifacts(Image *image)
97{
98  const char
99    *artifact,
100    *value;
101
102  (void) FormatLocaleFile(stdout,"  Image Artifacts:\n");
103  ResetImageArtifactIterator(image);
104  while ((artifact=GetNextImageArtifact(image)) != (const char *) NULL ) {
105    (void) FormatLocaleFile(stdout,"    %s: ",artifact);
106    value=GetImageArtifact(image,artifact);
107    if (value != (const char *) NULL)
108      (void) FormatLocaleFile(stdout,"%s\n",value);
109  }
110  ResetImageArtifactIterator(image);
111}
112#endif
113
114
115/*
116%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
117%                                                                             %
118%                                                                             %
119%                                                                             %
120+   P r o c e s s S c r i p t O p t i o n s                                   %
121%                                                                             %
122%                                                                             %
123%                                                                             %
124%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
125%
126%  ProcessScriptOptions() reads options and processes options as they are
127%  found in the given file, or pipeline.  The filename to open and read
128%  options is given as the 'index' argument of the argument array given.
129%
130%  Other arguments following index may be read by special script options
131%  as settings (strings), images, or as operations to be processed in various
132%  ways.   How they are treated is up to the script being processed.
133%
134%  Note that a script not 'return' to the command line processing, nor can
135%  they call (and return from) other scripts. At least not at this time.
136%
137%  There are no 'ProcessOptionFlags' control flags at this time.
138%
139%  The format of the ProcessScriptOptions method is:
140%
141%    void ProcessScriptOptions(MagickCLI *cli_wand,int argc,char **argv,
142%               int index)
143%
144%  A description of each parameter follows:
145%
146%    o cli_wand: the main CLI Wand to use.
147%
148%    o argc: the number of elements in the argument vector.
149%
150%    o argv: A text array containing the command line arguments.
151%
152%    o index: offset for argc to CLI argumnet count
153%
154*/
155WandExport void ProcessScriptOptions(MagickCLI *cli_wand,int argc,char **argv,
156     int index)
157{
158  ScriptTokenInfo
159    *token_info;
160
161  CommandOptionFlags
162    option_type;
163
164  int
165    count;
166
167  char
168    *option,
169    *arg1,
170    *arg2;
171
172  assert(argc>index); /* at least one argument - script name */
173  assert(argv != (char **)NULL);
174  assert(argv[index] != (char *)NULL);
175  assert(argv[argc-1] != (char *)NULL);
176  assert(cli_wand != (MagickCLI *) NULL);
177  assert(cli_wand->signature == WandSignature);
178  if (cli_wand->wand.debug != MagickFalse)
179    (void) LogMagickEvent(WandEvent,GetMagickModule(),"%s",cli_wand->wand.name);
180
181  /* open file script or stream, and set up tokenizer */
182  token_info = AcquireScriptTokenInfo(argv[index]);
183  if (token_info->token == (char *) NULL) {
184    ThrowFileException(cli_wand->wand.exception,OptionFatalError,
185               "UnableToOpenScript",argv[index]);
186    return;
187  }
188
189  /* define the error location string for use in exceptions
190     order of input escapes: option, filename, line, column
191  */
192  cli_wand->location="'%s' in \"%s\" line %u column %u";
193  if ( LocaleCompare("-", argv[index]) == 0 )
194    cli_wand->filename="stdin";
195  else
196    cli_wand->filename=argv[index];
197
198  /* Process Options from Script */
199  option = arg1 = arg2 = (char*)NULL;
200  while (1) {
201
202    /* Get a option */
203    { MagickBooleanType status = GetScriptToken(token_info);
204      cli_wand->line=token_info->token_line;
205      cli_wand->column=token_info->token_column;
206      if( status == MagickFalse )
207        break;
208    }
209
210    /* save option details */
211    CloneString(&option,token_info->token);
212
213    { /* get option type and argument count */
214      const OptionInfo *option_info = GetCommandOptionInfo(option);
215      count=option_info->type;
216      option_type=option_info->flags;
217#if MagickCommandDebug >= 2
218      (void) FormatLocaleFile(stderr, "Script: %u,%u: \"%s\" matched \"%s\"\n",
219             cli_wand->line, cli_wand->line, option, option_info->mnemonic );
220#endif
221    }
222
223    /* handle a undefined option - image read? */
224    if ( option_type == UndefinedOptionFlag ||
225         (option_type & NonMagickOptionFlag) != 0 ) {
226#if MagickCommandDebug
227      (void) FormatLocaleFile(stderr, "Script Non-Option: \"%s\"\n", option);
228#endif
229      if ( IsCommandOption(option) == MagickFalse)
230        /* non-option -- treat as a image read */
231        CLISpecialOperator(cli_wand,"-read",option);
232      else
233        CLIWandExceptionBreak(OptionFatalError,"UnrecognizedOption",option);
234      count = 0;
235      goto next_token;
236    }
237
238    if ( count >= 1 ) {
239      if( GetScriptToken(token_info) == MagickFalse )
240        CLIWandException(OptionFatalError,"MissingArgument",option);
241      CloneString(&arg1,token_info->token);
242    }
243    else
244      CloneString(&arg1,(char *)NULL);
245
246    if ( count >= 2 ) {
247      if( GetScriptToken(token_info) == MagickFalse )
248        CLIWandExceptionBreak(OptionFatalError,"MissingArgument",option);
249      CloneString(&arg2,token_info->token);
250    }
251    else
252      CloneString(&arg2,(char *)NULL);
253
254#if MagickCommandDebug
255    (void) FormatLocaleFile(stderr,
256        "Script Option: \"%s\" \tCount: %d  Flags: %04x  Args: \"%s\" \"%s\"\n",
257        option,(int) count,option_type,arg1,arg2);
258#endif
259
260    if ( (option_type & DeprecateOptionFlag) != 0 ) {
261      CLIWandException(OptionWarning,"DeprecatedOption",option);
262      if ( CLICatchException(cli_wand, MagickFalse) != MagickFalse )
263        break;
264    }
265
266    /* handle special script-argument options here */
267    //either continue processing command line
268    // or making use of the command line options.
269    //CLICommandOptions(cli_wand,count+1,argv, MagickScriptArgsFlags);
270
271    /* Process Option from file */
272    if ( (option_type & SpecialOptionFlag) != 0 ) {
273      if ( LocaleCompare(option,"-exit") == 0 )
274        break;
275      /* No "-script" option from script at this time - though posible */
276      CLISpecialOperator(cli_wand,option,arg1);
277    }
278
279    if ( (option_type & SettingOptionFlags) != 0 ) {
280      CLISettingOptionInfo(cli_wand, option, arg1);
281      // FUTURE: Sync Specific Settings into Image Properities (not global)
282    }
283
284    if ( (option_type & SimpleOperatorOptionFlag) != 0)
285      CLISimpleOperatorImages(cli_wand, option, arg1, arg2);
286
287    if ( (option_type & ListOperatorOptionFlag) != 0 )
288      CLIListOperatorImages(cli_wand, option, arg1, arg2);
289
290next_token:
291#if MagickCommandDebug >= 4
292    OutputOptions(cli_wand->wand.image_info);
293    if ( cli_wand->wand.images != (Image *)NULL )
294      OutputArtifacts(cli_wand->wand.images);
295#endif
296    if ( CLICatchException(cli_wand, MagickFalse) != MagickFalse )
297      break;
298  }
299
300#if MagickCommandDebug
301  (void) FormatLocaleFile(stderr, "Script End: %d\n", token_info->status);
302#endif
303  switch( token_info->status ) {
304    case TokenStatusOK:
305    case TokenStatusEOF:
306      break;
307    case TokenStatusBadQuotes:
308      /* Ensure last token has a sane length for error report */
309      if( strlen(token_info->token) > INITAL_TOKEN_LENGTH-1 ) {
310        token_info->token[INITAL_TOKEN_LENGTH-4] = '.';
311        token_info->token[INITAL_TOKEN_LENGTH-3] = '.';
312        token_info->token[INITAL_TOKEN_LENGTH-2] = '.';
313        token_info->token[INITAL_TOKEN_LENGTH-1] = '\0';
314      }
315      CLIWandException(OptionFatalError,"ScriptUnbalancedQuotes",
316           token_info->token);
317      break;
318    case TokenStatusMemoryFailed:
319      CLIWandException(OptionFatalError,"ScriptTokenMemoryFailed","");
320      break;
321    case TokenStatusBinary:
322      CLIWandException(OptionFatalError,"ScriptIsBinary","");
323      break;
324  }
325
326  /* Clean up */
327  token_info = DestroyScriptTokenInfo(token_info);
328
329  CloneString(&option,(char *)NULL);
330  CloneString(&arg1,(char *)NULL);
331  CloneString(&arg2,(char *)NULL);
332
333  return;
334}
335
336/*
337%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
338%                                                                             %
339%                                                                             %
340%                                                                             %
341+  P r o c e s s C o m m a n d O p t i o n s                                  %
342%                                                                             %
343%                                                                             %
344%                                                                             %
345%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
346%
347%  ProcessCommandOptions() reads and processes arguments in the given
348%  command line argument array. The array does not contain the command
349%  being processed, only the options.
350%
351%  The 'process_flags' can be used to control and limit option processing.
352%  For example, to only process one option, or how unknown and special options
353%  are to be handled, and if the last argument in array is to be regarded as a
354%  final image write argument (filename or special coder).
355%
356%  The format of the ProcessCommandOptions method is:
357%
358%    int ProcessCommandOptions(MagickCLI *cli_wand,int argc,char **argv,
359%           int index, ProcessOptionFlags process_flags )
360%
361%  A description of each parameter follows:
362%
363%    o cli_wand: the main CLI Wand to use.
364%
365%    o argc: the number of elements in the argument vector.
366%
367%    o argv: A text array containing the command line arguments.
368%
369%    o process_flags: What type of arguments we are allowed to process
370%
371%    o index: index in the argv array to start processing from
372%
373% The function returns the index ot the next option to be processed. This
374% is really only releven if process_flags contains a ProcessOneOptionOnly
375% flag.
376%
377*/
378WandExport int ProcessCommandOptions(MagickCLI *cli_wand, int argc,
379     char **argv, int index, ProcessOptionFlags process_flags )
380{
381  const char
382    *option,
383    *arg1,
384    *arg2;
385
386  int
387    i,
388    end,
389    count;
390
391  CommandOptionFlags
392    option_type;
393
394  assert(argc>=index); /* you may have no arguments left! */
395  assert(argv != (char **)NULL);
396  assert(argv[index] != (char *)NULL);
397  assert(argv[argc-1] != (char *)NULL);
398  assert(cli_wand != (MagickCLI *) NULL);
399  assert(cli_wand->signature == WandSignature);
400  if (cli_wand->wand.debug != MagickFalse)
401    (void) LogMagickEvent(WandEvent,GetMagickModule(),"%s",cli_wand->wand.name);
402
403  /*
404    Parse command-line options.
405  */
406  cli_wand->location="'%s' %s arg %d";
407  cli_wand->filename="CLI";
408
409  end = argc;
410  if ( ( process_flags & ProcessOutputFile ) != 0 )
411    end--;
412
413  for (i=index; i < end; i += count +1) {
414    /* Finished processing one option? */
415    if ( ( process_flags & ProcessOneOptionOnly ) != 0 && i != index )
416      return(i);
417
418    option=argv[i];
419    cli_wand->line=i;
420
421    { const OptionInfo *option_info = GetCommandOptionInfo(argv[i]);
422      count=option_info->type;
423      option_type=option_info->flags;
424#if MagickCommandDebug >= 2
425      (void) FormatLocaleFile(stderr, "CLI %d: \"%s\" matched \"%s\"\n",
426            i, argv[i], option_info->mnemonic );
427#endif
428    }
429
430    if ( option_type == UndefinedOptionFlag ||
431         (option_type & NonMagickOptionFlag) != 0 ) {
432#if MagickCommandDebug
433      (void) FormatLocaleFile(stderr, "CLI Non-Option: \"%s\"\n", option);
434#endif
435      if ( ( IsCommandOption(option) == MagickFalse ) &&
436         ( (process_flags & ProcessNonOptionImageRead) != 0 ) )
437        /* non-option -- treat as a image read */
438        CLISpecialOperator(cli_wand,"-read",option);
439      else if ( (process_flags & ProcessUnknownOptionError) != 0 )
440        CLIWandException(OptionFatalError,"UnrecognizedOption",option);
441      count = 0;
442      goto next_argument;
443    }
444
445    if ( (option_type & DeprecateOptionFlag) != 0 ) {
446      CLIWandException(OptionWarning,"DeprecatedOption",option);
447      if ( CLICatchException(cli_wand, MagickFalse) != MagickFalse )
448        return(i+count+1);
449    }
450    if ((i+count) >= end ) {
451      CLIWandException(OptionFatalError,"MissingArgument",option);
452      if ( CLICatchException(cli_wand, MagickFalse) != MagickFalse )
453        return(end);
454      goto next_argument; /* no more arguments unable to proceed */
455    }
456
457    arg1 = ( count >= 1 ) ? argv[i+1] : (char *)NULL;
458    arg2 = ( count >= 2 ) ? argv[i+2] : (char *)NULL;
459
460#if MagickCommandDebug
461    (void) FormatLocaleFile(stderr,
462        "CLI Option: \"%s\" \tCount: %d  Flags: %04x  Args: \"%s\" \"%s\"\n",
463        option,(int) count,option_type,arg1,arg2);
464#endif
465
466    if ( (option_type & SpecialOptionFlag) != 0 ) {
467      if ( ( process_flags & ProcessExitOption ) != 0
468           && LocaleCompare(option,"-exit") == 0 )
469        return(i+count);
470      if ( ( process_flags & ProcessScriptOption ) != 0
471           && LocaleCompare(option,"-script") == 0) {
472        // Unbalanced Parenthesis if stack not empty
473        // Call Script, with a filename as a zeroth argument
474        ProcessScriptOptions(cli_wand,argc,argv,i+1);
475        return(argc); /* no more options after script process! */
476      }
477      CLISpecialOperator(cli_wand,option,arg1);
478    }
479
480    if ( (option_type & SettingOptionFlags) != 0 ) {
481      CLISettingOptionInfo(cli_wand, option, arg1);
482      // FUTURE: Sync Specific Settings into Image Properities (not global)
483    }
484
485    if ( (option_type & SimpleOperatorOptionFlag) != 0)
486      CLISimpleOperatorImages(cli_wand, option, arg1, arg2);
487
488    if ( (option_type & ListOperatorOptionFlag) != 0 )
489      CLIListOperatorImages(cli_wand, option, arg1, arg2);
490
491next_argument:
492#if MagickCommandDebug >= 4
493    OutputOptions(cli_wand->wand.image_info);
494    if ( cli_wand->wand.images != (Image *)NULL )
495      OutputArtifacts(cli_wand->wand.images);
496#endif
497    if ( CLICatchException(cli_wand, MagickFalse) != MagickFalse )
498      return(i+count);
499  }
500  assert(i==end);
501
502  if ( ( process_flags & ProcessOutputFile ) == 0 )
503    return(end);
504
505  assert(end==argc-1);
506
507  /*
508     Implicit Write of images to final CLI argument
509  */
510  option=argv[i];
511  cli_wand->line=i;
512
513#if MagickCommandDebug
514  (void) FormatLocaleFile(stderr, "CLI Write File: \"%s\"\n", option );
515#endif
516
517  // if stacks are not empty
518  //  ThrowConvertException(OptionError,"UnbalancedParenthesis",option,i);
519
520  /* This is a valid 'do no write' option for a CLI */
521  if (LocaleCompare(option,"-exit") == 0 )
522    return(argc);  /* just exit, no image write */
523
524  /* If there is an option -- produce an error */
525  if (IsCommandOption(option) != MagickFalse) {
526    CLIWandException(OptionError,"MissingOutputFilename",option);
527    return(argc);
528  }
529
530  /* If no images in MagickCLI */
531  if ( cli_wand->wand.images == (Image *) NULL ) {
532    /* a "null:" output coder with no images is not an error! */
533    if ( LocaleCompare(option,"null:") == 0 )
534      return(argc);
535    CLIWandException(OptionError,"NoImagesForFinalWrite",option);
536    return(argc);
537  }
538
539#if 0
540  WandListOperatorImages(cli_wand,"-write",option,(const char *)NULL);
541#else
542  (void) SyncImagesSettings(cli_wand->wand.image_info,cli_wand->wand.images,
543       cli_wand->wand.exception);
544  (void) WriteImages(cli_wand->wand.image_info,cli_wand->wand.images,option,
545       cli_wand->wand.exception);
546#endif
547  return(argc);
548}
549
550/*
551%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
552%                                                                             %
553%                                                                             %
554%                                                                             %
555+   M a g i c k I m a g e C o m m a n d                                       %
556%                                                                             %
557%                                                                             %
558%                                                                             %
559%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
560%
561%  MagickImageCommand() Handle special use CLI arguments and prepare a
562%  CLI MagickCLI to process the command line or directly specified script.
563%
564%  This is essentualy interface function between the MagickCore library
565%  initialization function MagickCommandGenesis(), and the option MagickCLI
566%  processing functions  ProcessCommandOptions()  or  ProcessScriptOptions()
567%
568%  The format of the MagickImageCommand method is:
569%
570%      MagickBooleanType MagickImageCommand(ImageInfo *image_info,
571%           int argc, char **argv, char **metadata, ExceptionInfo *exception)
572%
573%  A description of each parameter follows:
574%
575%    o image_info: the starting image_info structure
576%         (for compatibilty with MagickCommandGenisis())
577%
578%    o argc: the number of elements in the argument vector.
579%
580%    o argv: A text array containing the command line arguments.
581%
582%    o metadata: any metadata is returned here.
583%         (for compatibilty with MagickCommandGenisis())
584%
585%    o exception: return any errors or warnings in this structure.
586%
587*/
588
589static MagickBooleanType MagickUsage(void)
590{
591  printf("Version: %s\n",GetMagickVersion((size_t *) NULL));
592  printf("Copyright: %s\n",GetMagickCopyright());
593  printf("Features: %s\n\n",GetMagickFeatures());
594  printf("\n");
595
596  printf("Usage: %s [(options|images) ...] output_image\n", GetClientName());
597  printf("       %s -script filename [script args...]\n", GetClientName());
598  printf("       ... | %s -script - | ...\n", GetClientName());
599  printf("\n");
600
601  printf("  For more information on usage, options, examples, and technqiues\n");
602  printf("  see the ImageMagick website at\n    %s\n", MagickAuthoritativeURL);
603  printf("  Or the web pages in ImageMagick Sources\n");
604  return(MagickFalse);
605}
606
607/*
608   Concatanate given file arguments to the given output argument.
609   Used for a special -concatenate option used for specific 'delegates'.
610   The option is not formally documented.
611
612      magick -concatenate files... output
613
614   This is much like the UNIX "cat" command, but for both UNIX and Windows,
615   however the last argument provides the output filename.
616*/
617static MagickBooleanType ConcatenateImages(int argc,char **argv,
618  ExceptionInfo *exception)
619{
620  FILE
621    *input,
622    *output;
623
624  int
625    c;
626
627  register ssize_t
628    i;
629
630  output=fopen_utf8(argv[argc-1],"wb");
631  if (output == (FILE *) NULL) {
632    ThrowFileException(exception,FileOpenError,"UnableToOpenFile",
633      argv[argc-1]);
634    return(MagickFalse);
635  }
636  for (i=2; i < (ssize_t) (argc-1); i++) {
637    input=fopen_utf8(argv[i],"rb");
638    if (input == (FILE *) NULL)
639      ThrowFileException(exception,FileOpenError,"UnableToOpenFile",argv[i]);
640    for (c=fgetc(input); c != EOF; c=fgetc(input))
641      (void) fputc((char) c,output);
642    (void) fclose(input);
643    (void) remove_utf8(argv[i]);
644  }
645  (void) fclose(output);
646  return(MagickTrue);
647}
648
649WandExport MagickBooleanType MagickImageCommand(ImageInfo *image_info,
650  int argc,char **argv,char **metadata,ExceptionInfo *exception)
651{
652  MagickCLI
653    *cli_wand;
654
655  const char
656    *option;
657
658  /* For specific OS command line requirements */
659  ReadCommandlLine(argc,&argv);
660
661#if 0
662  /* FUTURE: This does not make sense!  Remove it.
663     Only a 'image read' needs to expand file name glob patterns
664  */
665  status=ExpandFilenames(&argc,&argv);
666  if (status == MagickFalse)
667    ThrowConvertException(ResourceLimitError,"MemoryAllocationFailed",
668      GetExceptionMessage(errno));
669#endif
670
671  /* Handle special single use options */
672  if (argc == 2) {
673    option=argv[1];
674    if ((LocaleCompare("-version",option+1) == 0) ||
675        (LocaleCompare("--version",option+1) == 0) ) {
676      (void) FormatLocaleFile(stdout,"Version: %s\n",
677        GetMagickVersion((size_t *) NULL));
678      (void) FormatLocaleFile(stdout,"Copyright: %s\n",
679        GetMagickCopyright());
680      (void) FormatLocaleFile(stdout,"Features: %s\n\n",
681        GetMagickFeatures());
682      return(MagickFalse);
683    }
684  }
685
686  if (argc >= 2) {
687    /* Special "concatenate option (hidden) for delegate usage */
688    if (LocaleCompare("-concatenate",argv[1]) == 0)
689      return(ConcatenateImages(argc,argv,exception));
690
691    /* Special Handling for a "#!/usr/bin/env magick-script" script */
692    if (LocaleCompare("-script",argv[0]+strlen(argv[0])-7) == 0) {
693      cli_wand=AcquireMagickCLI(image_info,exception);
694      GetPathComponent(argv[1],TailPath,cli_wand->wand.name);
695      ProcessScriptOptions(cli_wand,argc,argv,1);
696      goto Magick_Command_Cleanup;
697    }
698  }
699
700  if (argc < 3)
701    return(MagickUsage());
702
703  /* Initialize special "CLI Wand" to hold images and settings (empty) */
704  cli_wand=AcquireMagickCLI(image_info,exception);
705
706  if (LocaleCompare("-list",argv[1]) == 0)
707    /* Special option, list information and exit
708       FUTURE: this should be a MagickCore option,
709       especially as no wand is actually needed!
710    */
711    CLISpecialOperator(cli_wand, argv[1], argv[2]);
712  else if (LocaleCompare("-script",argv[1]) == 0) {
713    /* Start processing directly from script, no pre-script options
714       Replace wand command name with script name
715       First argument in the argv array is the script name to read.
716    */
717    GetPathComponent(argv[2],TailPath,cli_wand->wand.name);
718    ProcessScriptOptions(cli_wand,argc,argv,2);
719  }
720  else {
721    /* Noraml Command Line, Assumes output file as last option */
722    GetPathComponent(argv[0],TailPath,cli_wand->wand.name);
723    ProcessCommandOptions(cli_wand,argc,argv,1,
724       (LocaleCompare("magick",cli_wand->wand.name) == 0)?
725           MagickCommandOptionFlags : ConvertCommandOptionFlags);
726  }
727
728Magick_Command_Cleanup:
729  /* recover original image_info from bottom of stack */
730  while (cli_wand->image_info_stack != (Stack *)NULL)
731    CLISpecialOperator(cli_wand,"}",(const char *)NULL);
732
733  /* assert we have recovered the original structures */
734  assert(cli_wand->wand.image_info == image_info);
735  assert(cli_wand->wand.exception == exception);
736
737  /* Handle metadata for ImageMagickObject COM object for Windows VBS */
738  if (metadata != (char **) NULL) {
739    const char
740      *format;
741
742    char
743      *text;
744
745    format="%w,%h,%m";   // Get this from image_info Option splaytree
746
747    text=InterpretImageProperties(image_info,cli_wand->wand.images,format,
748         exception);
749    if (text == (char *) NULL)
750      ThrowMagickException(exception,GetMagickModule(),ResourceLimitError,
751           "MemoryAllocationFailed","`%s'", GetExceptionMessage(errno));
752    else {
753      (void) ConcatenateString(&(*metadata),text);
754      text=DestroyString(text);
755    }
756  }
757
758  /* Destroy the special CLI Wand */
759  cli_wand->wand.image_info = (ImageInfo *)NULL; /* not these */
760  cli_wand->wand.exception = (ExceptionInfo *)NULL;
761  cli_wand=DestroyMagickCLI(cli_wand);
762
763  return((exception->severity > ErrorException) ? MagickFalse : MagickTrue);
764}
765