magick-cli.c revision 24aa882bb8f0a8555253f77fc9ad8cb12fcb8c05
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    /* Sanity check: option is larger than anything that should be posible */
211    if( strlen(token_info->token) > INITAL_TOKEN_LENGTH-1 ) {
212      token_info->token[INITAL_TOKEN_LENGTH-4] = '.';
213      token_info->token[INITAL_TOKEN_LENGTH-3] = '.';
214      token_info->token[INITAL_TOKEN_LENGTH-2] = '.';
215      token_info->token[INITAL_TOKEN_LENGTH-1] = '\0';
216      CLIWandException(OptionFatalError,"UnrecognizedOption",token_info->token);
217      break;
218    }
219
220    /* save option details */
221    CloneString(&option,token_info->token);
222
223    { /* get option type and argument count */
224      const OptionInfo *option_info = GetCommandOptionInfo(option);
225      count=option_info->type;
226      option_type=option_info->flags;
227#if MagickCommandDebug >= 2
228      (void) FormatLocaleFile(stderr, "Script: %u,%u: \"%s\" matched \"%s\"\n",
229             cli_wand->line, cli_wand->line, option, option_info->mnemonic );
230#endif
231    }
232
233    /* handle a undefined option - image read? */
234    if ( option_type == UndefinedOptionFlag ||
235         (option_type & NonMagickOptionFlag) != 0 ) {
236#if MagickCommandDebug
237      (void) FormatLocaleFile(stderr, "Script Non-Option: \"%s\"\n", option);
238#endif
239      if ( IsCommandOption(option) == MagickFalse)
240        /* non-option -- treat as a image read */
241        CLISpecialOperator(cli_wand,"-read",option);
242      else
243        CLIWandExceptionBreak(OptionFatalError,"UnrecognizedOption",option);
244      count = 0;
245      goto next_token;
246    }
247
248    if ( count >= 1 ) {
249      if( GetScriptToken(token_info) == MagickFalse )
250        CLIWandException(OptionFatalError,"MissingArgument",option);
251      CloneString(&arg1,token_info->token);
252    }
253    else
254      CloneString(&arg1,(char *)NULL);
255
256    if ( count >= 2 ) {
257      if( GetScriptToken(token_info) == MagickFalse )
258        CLIWandExceptionBreak(OptionFatalError,"MissingArgument",option);
259      CloneString(&arg2,token_info->token);
260    }
261    else
262      CloneString(&arg2,(char *)NULL);
263
264    /* handle script special options here */
265    //either continue processing command line
266    // or making use of the command line options.
267    //CLICommandOptions(cli_wand,count+1,argv, MagickScriptArgsFlags);
268
269#if MagickCommandDebug
270    (void) FormatLocaleFile(stderr,
271        "Script Option: \"%s\" \tCount: %d  Flags: %04x  Args: \"%s\" \"%s\"\n",
272        option,(int) count,option_type,arg1,arg2);
273#endif
274
275    /* Process non-script specific option from file */
276    if ( (option_type & SpecialOptionFlag) != 0 ) {
277      if ( LocaleCompare(option,"-exit") == 0 )
278        break;
279      /* No "-script" option from script at this time */
280      CLISpecialOperator(cli_wand,option,arg1);
281    }
282
283    if ( (option_type & SettingOptionFlags) != 0 ) {
284      CLISettingOptionInfo(cli_wand, option, arg1);
285      // FUTURE: Sync Specific Settings into Image Properities (not global)
286    }
287
288    if ( (option_type & SimpleOperatorOptionFlag) != 0)
289      CLISimpleOperatorImages(cli_wand, option, arg1, arg2);
290
291    if ( (option_type & ListOperatorOptionFlag) != 0 )
292      CLIListOperatorImages(cli_wand, option, arg1, arg2);
293
294next_token:
295#if MagickCommandDebug >= 4
296    OutputOptions(cli_wand->wand.image_info);
297    if ( cli_wand->wand.images != (Image *)NULL )
298      OutputArtifacts(cli_wand->wand.images);
299#endif
300    if ( CLICatchException(cli_wand, MagickFalse) != MagickFalse )
301      break;
302  }
303
304#if MagickCommandDebug
305  (void) FormatLocaleFile(stderr, "Script End: %d\n", token_info->status);
306#endif
307  switch( token_info->status ) {
308    case TokenStatusOK:
309    case TokenStatusEOF:
310      break;
311    case TokenStatusBadQuotes:
312      /* Ensure last token has a sane length for error report */
313      if( strlen(token_info->token) > INITAL_TOKEN_LENGTH-1 ) {
314        token_info->token[INITAL_TOKEN_LENGTH-4] = '.';
315        token_info->token[INITAL_TOKEN_LENGTH-3] = '.';
316        token_info->token[INITAL_TOKEN_LENGTH-2] = '.';
317        token_info->token[INITAL_TOKEN_LENGTH-1] = '\0';
318      }
319      CLIWandException(OptionFatalError,"ScriptUnbalancedQuotes",
320           token_info->token);
321      break;
322    case TokenStatusMemoryFailed:
323      CLIWandException(OptionFatalError,"ScriptTokenMemoryFailed","");
324      break;
325    case TokenStatusBinary:
326      CLIWandException(OptionFatalError,"ScriptIsBinary","");
327      break;
328  }
329
330  /* Clean up */
331  token_info = DestroyScriptTokenInfo(token_info);
332
333  CloneString(&option,(char *)NULL);
334  CloneString(&arg1,(char *)NULL);
335  CloneString(&arg2,(char *)NULL);
336
337  return;
338}
339
340/*
341%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
342%                                                                             %
343%                                                                             %
344%                                                                             %
345+  P r o c e s s C o m m a n d O p t i o n s                                  %
346%                                                                             %
347%                                                                             %
348%                                                                             %
349%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
350%
351%  ProcessCommandOptions() reads and processes arguments in the given
352%  command line argument array. The array does not contain the command
353%  being processed, only the options.
354%
355%  The 'process_flags' can be used to control and limit option processing.
356%  For example, to only process one option, or how unknown and special options
357%  are to be handled, and if the last argument in array is to be regarded as a
358%  final image write argument (filename or special coder).
359%
360%  The format of the ProcessCommandOptions method is:
361%
362%    int ProcessCommandOptions(MagickCLI *cli_wand,int argc,char **argv,
363%           int index, ProcessOptionFlags process_flags )
364%
365%  A description of each parameter follows:
366%
367%    o cli_wand: the main CLI Wand to use.
368%
369%    o argc: the number of elements in the argument vector.
370%
371%    o argv: A text array containing the command line arguments.
372%
373%    o process_flags: What type of arguments we are allowed to process
374%
375%    o index: index in the argv array to start processing from
376%
377% The function returns the index ot the next option to be processed. This
378% is really only releven if process_flags contains a ProcessOneOptionOnly
379% flag.
380%
381*/
382WandExport int ProcessCommandOptions(MagickCLI *cli_wand, int argc,
383     char **argv, int index, ProcessOptionFlags process_flags )
384{
385  const char
386    *option,
387    *arg1,
388    *arg2;
389
390  int
391    i,
392    end,
393    count;
394
395  CommandOptionFlags
396    option_type;
397
398  assert(argc>=index); /* you may have no arguments left! */
399  assert(argv != (char **)NULL);
400  assert(argv[index] != (char *)NULL);
401  assert(argv[argc-1] != (char *)NULL);
402  assert(cli_wand != (MagickCLI *) NULL);
403  assert(cli_wand->signature == WandSignature);
404  if (cli_wand->wand.debug != MagickFalse)
405    (void) LogMagickEvent(WandEvent,GetMagickModule(),"%s",cli_wand->wand.name);
406
407  /*
408    Parse command-line options.
409  */
410  cli_wand->location="'%s' %s arg %d";
411  cli_wand->filename="CLI";
412
413  end = argc;
414  if ( ( process_flags & ProcessOutputFile ) != 0 )
415    end--;
416
417  for (i=index; i < end; i += count +1) {
418    /* Finished processing one option? */
419    if ( ( process_flags & ProcessOneOptionOnly ) != 0 && i != index )
420      return(i);
421
422    option=argv[i];
423    cli_wand->line=i;
424
425    { const OptionInfo *option_info = GetCommandOptionInfo(argv[i]);
426      count=option_info->type;
427      option_type=option_info->flags;
428#if MagickCommandDebug >= 2
429      (void) FormatLocaleFile(stderr, "CLI %d: \"%s\" matched \"%s\"\n",
430            i, argv[i], option_info->mnemonic );
431#endif
432    }
433
434    if ( option_type == UndefinedOptionFlag ||
435         (option_type & NonMagickOptionFlag) != 0 ) {
436#if MagickCommandDebug
437      (void) FormatLocaleFile(stderr, "CLI Non-Option: \"%s\"\n", option);
438#endif
439      if ( ( IsCommandOption(option) == MagickFalse ) &&
440         ( (process_flags & ProcessNonOptionImageRead) != 0 ) )
441        /* non-option -- treat as a image read */
442        CLISpecialOperator(cli_wand,"-read",option);
443      else if ( (process_flags & ProcessUnknownOptionError) != 0 )
444        CLIWandException(OptionFatalError,"UnrecognizedOption",option);
445      count = 0;
446      goto next_argument;
447    }
448
449    if ( (option_type & DeprecateOptionFlag) != 0 ) {
450      CLIWandException(OptionWarning,"DeprecatedOption",option);
451      if ( CLICatchException(cli_wand, MagickFalse) != MagickFalse )
452        return(i+count+1);
453    }
454    if ((i+count) >= end ) {
455      CLIWandException(OptionFatalError,"MissingArgument",option);
456      if ( CLICatchException(cli_wand, MagickFalse) != MagickFalse )
457        return(end);
458      goto next_argument; /* no more arguments unable to proceed */
459    }
460
461    arg1 = ( count >= 1 ) ? argv[i+1] : (char *)NULL;
462    arg2 = ( count >= 2 ) ? argv[i+2] : (char *)NULL;
463
464#if MagickCommandDebug
465    (void) FormatLocaleFile(stderr,
466        "CLI Option: \"%s\" \tCount: %d  Flags: %04x  Args: \"%s\" \"%s\"\n",
467        option,(int) count,option_type,arg1,arg2);
468#endif
469
470    if ( (option_type & SpecialOptionFlag) != 0 ) {
471      if ( ( process_flags & ProcessExitOption ) != 0
472           && LocaleCompare(option,"-exit") == 0 )
473        return(i+count);
474      if ( ( process_flags & ProcessScriptOption ) != 0
475           && LocaleCompare(option,"-script") == 0) {
476        // Unbalanced Parenthesis if stack not empty
477        // Call Script, with a filename as a zeroth argument
478        ProcessScriptOptions(cli_wand,argc,argv,i+1);
479        return(argc); /* no more options after script process! */
480      }
481      CLISpecialOperator(cli_wand,option,arg1);
482    }
483
484    if ( (option_type & SettingOptionFlags) != 0 ) {
485      CLISettingOptionInfo(cli_wand, option, arg1);
486      // FUTURE: Sync Specific Settings into Image Properities (not global)
487    }
488
489    if ( (option_type & SimpleOperatorOptionFlag) != 0)
490      CLISimpleOperatorImages(cli_wand, option, arg1, arg2);
491
492    if ( (option_type & ListOperatorOptionFlag) != 0 )
493      CLIListOperatorImages(cli_wand, option, arg1, arg2);
494
495next_argument:
496#if MagickCommandDebug >= 4
497    OutputOptions(cli_wand->wand.image_info);
498    if ( cli_wand->wand.images != (Image *)NULL )
499      OutputArtifacts(cli_wand->wand.images);
500#endif
501    if ( CLICatchException(cli_wand, MagickFalse) != MagickFalse )
502      return(i+count);
503  }
504  assert(i==end);
505
506  if ( ( process_flags & ProcessOutputFile ) == 0 )
507    return(end);
508
509  assert(end==argc-1);
510
511  /*
512     Implicit Write of images to final CLI argument
513  */
514  option=argv[i];
515
516#if MagickCommandDebug
517  (void) FormatLocaleFile(stderr, "CLI Write File: \"%s\"\n", option );
518#endif
519
520  // if stacks are not empty
521  //  ThrowConvertException(OptionError,"UnbalancedParenthesis",option,i);
522
523  /* This is a valid 'do no write' option for a CLI */
524  if (LocaleCompare(option,"-exit") == 0 )
525    return(argc);  /* just exit, no image write */
526
527  /* If there is an option -- produce an error */
528  if (IsCommandOption(option) != MagickFalse) {
529    CLIWandException(OptionError,"MissingOutputFilename",option);
530    return(argc);
531  }
532
533  /* If no images in MagickCLI */
534  if ( cli_wand->wand.images == (Image *) NULL ) {
535    /* a "null:" output coder with no images is not an error! */
536    if ( LocaleCompare(option,"null:") == 0 )
537      return(argc);
538    CLIWandException(OptionError,"NoImagesForFinalWrite",option);
539    return(argc);
540  }
541
542#if 0
543  WandListOperatorImages(cli_wand,"-write",option,(const char *)NULL);
544#else
545  (void) SyncImagesSettings(cli_wand->wand.image_info,cli_wand->wand.images,
546       cli_wand->wand.exception);
547  (void) WriteImages(cli_wand->wand.image_info,cli_wand->wand.images,option,
548       cli_wand->wand.exception);
549#endif
550  return(argc);
551}
552
553/*
554%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
555%                                                                             %
556%                                                                             %
557%                                                                             %
558+   M a g i c k I m a g e C o m m a n d                                       %
559%                                                                             %
560%                                                                             %
561%                                                                             %
562%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
563%
564%  MagickImageCommand() Handle special use CLI arguments and prepare a
565%  CLI MagickCLI to process the command line or directly specified script.
566%
567%  This is essentualy interface function between the MagickCore library
568%  initialization function MagickCommandGenesis(), and the option MagickCLI
569%  processing functions  ProcessCommandOptions()  or  ProcessScriptOptions()
570%
571%  The format of the MagickImageCommand method is:
572%
573%      MagickBooleanType MagickImageCommand(ImageInfo *image_info,
574%           int argc, char **argv, char **metadata, ExceptionInfo *exception)
575%
576%  A description of each parameter follows:
577%
578%    o image_info: the starting image_info structure
579%         (for compatibilty with MagickCommandGenisis())
580%
581%    o argc: the number of elements in the argument vector.
582%
583%    o argv: A text array containing the command line arguments.
584%
585%    o metadata: any metadata is returned here.
586%         (for compatibilty with MagickCommandGenisis())
587%
588%    o exception: return any errors or warnings in this structure.
589%
590*/
591
592static MagickBooleanType MagickUsage(void)
593{
594  printf("Version: %s\n",GetMagickVersion((size_t *) NULL));
595  printf("Copyright: %s\n",GetMagickCopyright());
596  printf("Features: %s\n\n",GetMagickFeatures());
597  printf("\n");
598
599  printf("Usage: %s [(options|images) ...] output_image\n", GetClientName());
600  printf("       %s -script filename [script args...]\n", GetClientName());
601  printf("       ... | %s -script - | ...\n", GetClientName());
602  printf("\n");
603
604  printf("  For more information on usage, options, examples, and technqiues\n");
605  printf("  see the ImageMagick website at\n    %s\n", MagickAuthoritativeURL);
606  printf("  Or the web pages in ImageMagick Sources\n");
607  return(MagickFalse);
608}
609
610/*
611   Concatanate given file arguments to the given output argument.
612   Used for a special -concatenate option used for specific 'delegates'.
613   The option is not formally documented.
614
615      magick -concatenate files... output
616
617   This is much like the UNIX "cat" command, but for both UNIX and Windows,
618   however the last argument provides the output filename.
619*/
620static MagickBooleanType ConcatenateImages(int argc,char **argv,
621  ExceptionInfo *exception)
622{
623  FILE
624    *input,
625    *output;
626
627  int
628    c;
629
630  register ssize_t
631    i;
632
633  output=fopen_utf8(argv[argc-1],"wb");
634  if (output == (FILE *) NULL) {
635    ThrowFileException(exception,FileOpenError,"UnableToOpenFile",
636      argv[argc-1]);
637    return(MagickFalse);
638  }
639  for (i=2; i < (ssize_t) (argc-1); i++) {
640    input=fopen_utf8(argv[i],"rb");
641    if (input == (FILE *) NULL)
642      ThrowFileException(exception,FileOpenError,"UnableToOpenFile",argv[i]);
643    for (c=fgetc(input); c != EOF; c=fgetc(input))
644      (void) fputc((char) c,output);
645    (void) fclose(input);
646    (void) remove_utf8(argv[i]);
647  }
648  (void) fclose(output);
649  return(MagickTrue);
650}
651
652WandExport MagickBooleanType MagickImageCommand(ImageInfo *image_info,
653  int argc,char **argv,char **metadata,ExceptionInfo *exception)
654{
655  MagickCLI
656    *cli_wand;
657
658  const char
659    *option;
660
661  /* Handle special single use options */
662  if (argc == 2) {
663    option=argv[1];
664    if ((LocaleCompare("-version",option+1) == 0) ||
665        (LocaleCompare("--version",option+1) == 0) ) {
666      (void) FormatLocaleFile(stdout,"Version: %s\n",
667        GetMagickVersion((size_t *) NULL));
668      (void) FormatLocaleFile(stdout,"Copyright: %s\n",
669        GetMagickCopyright());
670      (void) FormatLocaleFile(stdout,"Features: %s\n\n",
671        GetMagickFeatures());
672      return(MagickFalse);
673    }
674  }
675  /* The "magick" command must have at least two arguments */
676  if (argc < 3)
677    return(MagickUsage());
678  ReadCommandlLine(argc,&argv);
679
680#if 0
681  /* FUTURE: This does not make sense!  Remove it.
682     Only a 'image read' needs to expand file name glob patterns
683  */
684  status=ExpandFilenames(&argc,&argv);
685  if (status == MagickFalse)
686    ThrowConvertException(ResourceLimitError,"MemoryAllocationFailed",
687      GetExceptionMessage(errno));
688#endif
689
690  /* Special option (hidden) for delegate usage - no wand needed */
691  if (LocaleCompare("-concatenate",argv[1]) == 0)
692    return(ConcatenateImages(argc,argv,exception));
693
694  /* Initialize special "CLI Wand" to hold images and settings (empty) */
695  /* FUTURE: add this to 'operations.c' */
696  cli_wand=AcquireMagickCLI(image_info,exception);
697
698  if (LocaleCompare("-list",argv[1]) == 0)
699    /* Special option, list information and exit
700       FUTURE: this should be a MagickCore option,
701       especially as no wand is actually needed!
702    */
703    CLISpecialOperator(cli_wand, argv[1], argv[2]);
704  else if (LocaleCompare("-script",argv[1]) == 0) {
705    /* Start processing directly from script, no pre-script options
706       Replace wand command name with script name
707       First argument in the argv array is the script name to read.
708    */
709    GetPathComponent(argv[2],TailPath,cli_wand->wand.name);
710    ProcessScriptOptions(cli_wand,argc,argv,2);
711  }
712  else {
713    /* Processing Command line, assuming output file as last option */
714    GetPathComponent(argv[0],TailPath,cli_wand->wand.name);
715    ProcessCommandOptions(cli_wand,argc,argv,1,MagickCommandOptionFlags);
716  }
717
718  /* recover original image_info from bottom of stack */
719  while (cli_wand->image_info_stack != (Stack *)NULL)
720    CLISpecialOperator(cli_wand,"}",(const char *)NULL);
721
722  /* assert we have recovered the original structures */
723  assert(cli_wand->wand.image_info == image_info);
724  assert(cli_wand->wand.exception == exception);
725
726  /* Handle metadata for ImageMagickObject COM object for Windows VBS */
727  if (metadata != (char **) NULL) {
728    const char
729      *format;
730
731    char
732      *text;
733
734    format="%w,%h,%m";   // Get this from image_info Option splaytree
735
736    text=InterpretImageProperties(image_info,cli_wand->wand.images,format,
737         exception);
738    if (text == (char *) NULL)
739      ThrowMagickException(exception,GetMagickModule(),ResourceLimitError,
740           "MemoryAllocationFailed","`%s'", GetExceptionMessage(errno));
741    else {
742      (void) ConcatenateString(&(*metadata),text);
743      text=DestroyString(text);
744    }
745  }
746  /* Destroy the special CLI Wand */
747  cli_wand->wand.image_info = (ImageInfo *)NULL; /* not these */
748  cli_wand->wand.exception = (ExceptionInfo *)NULL;
749  cli_wand=DestroyMagickCLI(cli_wand);
750
751  return((exception->severity > ErrorException) ? MagickFalse : MagickTrue);
752}
753