magick-cli.c revision 0ea037a49d6f7f926e2cabb90c1ebcbf6b1c39bf
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 == (ScriptTokenInfo *) 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, (arg), filename, line, column */
191  cli_wand->location="'%s' in \"%s\" line %u column %u";
192  cli_wand->location2="'%s' '%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( IfMagickFalse(status) )
207        break; /* error or end of options */
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=(CommandOptionFlags) 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 ( IfMagickFalse(IsCommandOption(option)))
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( IfMagickFalse(GetScriptToken(token_info)) )
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( IfMagickFalse(GetScriptToken(token_info)) )
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    if ( cli_wand->wand.images != (Image *)NULL )
284      SyncImagesSettings(cli_wand->wand.image_info,cli_wand->wand.images,
285           cli_wand->wand.exception);
286
287    if ( (option_type & SimpleOperatorOptionFlag) != 0)
288      CLISimpleOperatorImages(cli_wand, option, arg1, arg2);
289
290    if ( (option_type & ListOperatorOptionFlag) != 0 )
291      CLIListOperatorImages(cli_wand, option, arg1, arg2);
292
293next_token:
294#if MagickCommandDebug >= 4
295    OutputOptions(cli_wand->wand.image_info);
296    if ( cli_wand->wand.images != (Image *)NULL )
297      OutputArtifacts(cli_wand->wand.images);
298#endif
299    if ( CLICatchException(cli_wand, MagickFalse) != MagickFalse )
300      break;
301  }
302
303#if MagickCommandDebug
304  (void) FormatLocaleFile(stderr, "Script End: %d\n", token_info->status);
305#endif
306  switch( token_info->status ) {
307    case TokenStatusOK:
308    case TokenStatusEOF:
309      break;
310    case TokenStatusBadQuotes:
311      /* Ensure last token has a sane length for error report */
312      if( strlen(token_info->token) > INITAL_TOKEN_LENGTH-1 ) {
313        token_info->token[INITAL_TOKEN_LENGTH-4] = '.';
314        token_info->token[INITAL_TOKEN_LENGTH-3] = '.';
315        token_info->token[INITAL_TOKEN_LENGTH-2] = '.';
316        token_info->token[INITAL_TOKEN_LENGTH-1] = '\0';
317      }
318      CLIWandException(OptionFatalError,"ScriptUnbalancedQuotes",
319           token_info->token);
320      break;
321    case TokenStatusMemoryFailed:
322      CLIWandException(OptionFatalError,"ScriptTokenMemoryFailed","");
323      break;
324    case TokenStatusBinary:
325      CLIWandException(OptionFatalError,"ScriptIsBinary","");
326      break;
327  }
328
329  /* Clean up */
330  token_info = DestroyScriptTokenInfo(token_info);
331
332  CloneString(&option,(char *)NULL);
333  CloneString(&arg1,(char *)NULL);
334  CloneString(&arg2,(char *)NULL);
335
336  return;
337}
338
339/*
340%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
341%                                                                             %
342%                                                                             %
343%                                                                             %
344+  P r o c e s s C o m m a n d O p t i o n s                                  %
345%                                                                             %
346%                                                                             %
347%                                                                             %
348%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
349%
350%  ProcessCommandOptions() reads and processes arguments in the given
351%  command line argument array. The array does not contain the command
352%  being processed, only the options.
353%
354%  The 'process_flags' can be used to control and limit option processing.
355%  For example, to only process one option, or how unknown and special options
356%  are to be handled, and if the last argument in array is to be regarded as a
357%  final image write argument (filename or special coder).
358%
359%  The format of the ProcessCommandOptions method is:
360%
361%    int ProcessCommandOptions(MagickCLI *cli_wand,int argc,char **argv,
362%           int index, ProcessOptionFlags process_flags )
363%
364%  A description of each parameter follows:
365%
366%    o cli_wand: the main CLI Wand to use.
367%
368%    o argc: the number of elements in the argument vector.
369%
370%    o argv: A text array containing the command line arguments.
371%
372%    o process_flags: What type of arguments will be processed, ignored
373%                     or return errors.
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  /* define the error location string for use in exceptions
408     order of input escapes: option, (arg), filename, line, column */
409  cli_wand->location="'%s' %s arg %u";
410  cli_wand->location2="'%s' '%s' %s arg %u";
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=(CommandOptionFlags) 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 ( IfMagickFalse(IsCommandOption(option) ) &&
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    if ( cli_wand->wand.images != (Image *)NULL )
489      SyncImagesSettings(cli_wand->wand.image_info,cli_wand->wand.images,
490          cli_wand->wand.exception);
491
492    if ( (option_type & SimpleOperatorOptionFlag) != 0)
493      CLISimpleOperatorImages(cli_wand, option, arg1, arg2);
494
495    if ( (option_type & ListOperatorOptionFlag) != 0 )
496      CLIListOperatorImages(cli_wand, option, arg1, arg2);
497
498next_argument:
499#if MagickCommandDebug >= 4
500    OutputOptions(cli_wand->wand.image_info);
501    if ( cli_wand->wand.images != (Image *)NULL )
502      OutputArtifacts(cli_wand->wand.images);
503#endif
504    if ( CLICatchException(cli_wand, MagickFalse) != MagickFalse )
505      return(i+count);
506  }
507  assert(i==end);
508
509  if ( ( process_flags & ProcessOutputFile ) == 0 )
510    return(end);
511
512  assert(end==argc-1);
513
514  /*
515     Implicit Write of images to final CLI argument
516  */
517  option=argv[i];
518  cli_wand->line=i;
519
520#if MagickCommandDebug
521  (void) FormatLocaleFile(stderr, "CLI Write File: \"%s\"\n", option );
522#endif
523
524  // if stacks are not empty
525  //  ThrowConvertException(OptionError,"UnbalancedParenthesis",option,i);
526
527  /* This is a valid 'do no write' option for a CLI */
528  if (LocaleCompare(option,"-exit") == 0 )
529    return(argc);  /* just exit, no image write */
530
531  /* If there is an option -- produce an error */
532  if (IsCommandOption(option) != MagickFalse) {
533    CLIWandException(OptionError,"MissingOutputFilename",option);
534    return(argc);
535  }
536
537  /* If no images in MagickCLI */
538  if ( cli_wand->wand.images == (Image *) NULL ) {
539    /* a "null:" output coder with no images is not an error! */
540    if ( LocaleCompare(option,"null:") == 0 )
541      return(argc);
542    CLIWandException(OptionError,"NoImagesForFinalWrite",option);
543    return(argc);
544  }
545
546#if 0
547  WandListOperatorImages(cli_wand,"-write",option,(const char *)NULL);
548#else
549  (void) SyncImagesSettings(cli_wand->wand.image_info,cli_wand->wand.images,
550       cli_wand->wand.exception);
551  (void) WriteImages(cli_wand->wand.image_info,cli_wand->wand.images,option,
552       cli_wand->wand.exception);
553#endif
554  return(argc);
555}
556
557/*
558%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
559%                                                                             %
560%                                                                             %
561%                                                                             %
562+   M a g i c k I m a g e C o m m a n d                                       %
563%                                                                             %
564%                                                                             %
565%                                                                             %
566%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
567%
568%  MagickImageCommand() Handle special use CLI arguments and prepare a
569%  CLI MagickCLI to process the command line or directly specified script.
570%
571%  This is essentualy interface function between the MagickCore library
572%  initialization function MagickCommandGenesis(), and the option MagickCLI
573%  processing functions  ProcessCommandOptions()  or  ProcessScriptOptions()
574%
575%  The format of the MagickImageCommand method is:
576%
577%      MagickBooleanType MagickImageCommand(ImageInfo *image_info,
578%           int argc, char **argv, char **metadata, ExceptionInfo *exception)
579%
580%  A description of each parameter follows:
581%
582%    o image_info: the starting image_info structure
583%         (for compatibilty with MagickCommandGenisis())
584%
585%    o argc: the number of elements in the argument vector.
586%
587%    o argv: A text array containing the command line arguments.
588%
589%    o metadata: any metadata (for VBS) is returned here.
590%         (for compatibilty with MagickCommandGenisis())
591%
592%    o exception: return any errors or warnings in this structure.
593%
594*/
595
596static MagickBooleanType MagickUsage(void)
597{
598  printf("Usage: %s [(options|images) ...] output_image\n", GetClientName());
599  printf("       %s -script filename [script args...]\n", GetClientName());
600  printf("\n");
601  printf("  For more information on usage, options, examples, and technqiues\n");
602  printf("  see the ImageMagick website at\n    %s\n", MagickAuthoritativeURL);
603  return(MagickFalse);
604}
605
606/*
607   Concatanate given file arguments to the given output argument.
608   Used for a special -concatenate option used for specific 'delegates'.
609   The option is not formally documented.
610
611      magick -concatenate files... output
612
613   This is much like the UNIX "cat" command, but for both UNIX and Windows,
614   however the last argument provides the output filename.
615*/
616static MagickBooleanType ConcatenateImages(int argc,char **argv,
617  ExceptionInfo *exception)
618{
619  FILE
620    *input,
621    *output;
622
623  int
624    c;
625
626  register ssize_t
627    i;
628
629  output=fopen_utf8(argv[argc-1],"wb");
630  if (output == (FILE *) NULL) {
631    ThrowFileException(exception,FileOpenError,"UnableToOpenFile",
632      argv[argc-1]);
633    return(MagickFalse);
634  }
635  for (i=2; i < (ssize_t) (argc-1); i++) {
636    input=fopen_utf8(argv[i],"rb");
637    if (input == (FILE *) NULL)
638      ThrowFileException(exception,FileOpenError,"UnableToOpenFile",argv[i]);
639    for (c=fgetc(input); c != EOF; c=fgetc(input))
640      (void) fputc((char) c,output);
641    (void) fclose(input);
642    (void) remove_utf8(argv[i]);
643  }
644  (void) fclose(output);
645  return(MagickTrue);
646}
647
648WandExport MagickBooleanType MagickImageCommand(ImageInfo *image_info,
649  int argc,char **argv,char **metadata,ExceptionInfo *exception)
650{
651  MagickCLI
652    *cli_wand;
653
654  const char
655    *option;
656
657  ProcessOptionFlags
658    process_flags = MagickCommandOptionFlags;
659
660  /* For specific OS command line requirements */
661  ReadCommandlLine(argc,&argv);
662
663#if 0
664  status=ExpandFilenames(&argc,&argv);
665  if ( IfMagickFalse(status) )
666    ThrowConvertException(ResourceLimitError,"MemoryAllocationFailed",
667      GetExceptionMessage(errno));
668#endif
669
670  /* Initialize special "CLI Wand" to hold images and settings (empty) */
671  cli_wand=AcquireMagickCLI(image_info,exception);
672
673  GetPathComponent(argv[0],TailPath,cli_wand->wand.name);
674  ConcatenateMagickString(cli_wand->wand.name,"-CLI",MaxTextExtent);
675
676  /* "convert" command - give a "depreciation" warning" */
677  if ( (LocaleCompare("convert",argv[0]+strlen((argv[0])-7)) == 0) ||
678       (LocaleNCompare("convert",argv[0],7) == 0) ||
679       (LocaleNCompare("lt-convert",argv[0],10) == 0) ) {
680    process_flags = ConvertCommandOptionFlags;
681    /*(void) FormatLocaleFile(stderr,"WARNING: %s\n",
682             "The convert is depreciated in IMv7, use \"magick\"\n");*/
683  }
684
685  /* Special Case:  If command name ends with "script" then run it as is
686     a "-script" option is implied.  This allows you to name the "magick"
687     command "magick-script", and create scripts of the form...
688           #!/usr/bin/env magick-script
689  */
690  if (LocaleCompare("script",argv[0]+strlen(argv[0])-6) == 0) {
691    GetPathComponent(argv[1],TailPath,cli_wand->wand.name);
692    ProcessScriptOptions(cli_wand,argc,argv,1);
693    goto Magick_Command_Cleanup;
694  }
695
696  /* Special Case: Version Information and Abort */
697  if (argc == 2) {
698    option=argv[1];
699    if ((LocaleCompare("-version",option) == 0) ||
700        (LocaleCompare("--version",option) == 0) ) {
701      CLISpecialOperator(cli_wand, "-version", (char *)NULL);
702      goto Magick_Command_Exit;
703    }
704  }
705
706  /* not enough arguments -- including -help */
707  if (argc < 3) {
708    CLISpecialOperator(cli_wand, "-version", (char *)NULL);
709    MagickUsage();
710    goto Magick_Command_Exit;
711  }
712
713  /* List Information and Abort */
714  if (LocaleCompare("-list",argv[1]) == 0) {
715    CLISpecialOperator(cli_wand, argv[1], argv[2]);
716    goto Magick_Command_Exit;
717  }
718
719  /* Special "concatenate option (hidden) for delegate usage */
720  if (LocaleCompare("-concatenate",argv[1]) == 0) {
721    ConcatenateImages(argc,argv,exception);
722    goto Magick_Command_Exit;
723  }
724
725  /* ------------- */
726  /* The Main Call */
727
728  if (LocaleCompare("-script",argv[1]) == 0) {
729    /* Start processing directly from script, no pre-script options
730       Replace wand command name with script name
731       First argument in the argv array is the script name to read.
732    */
733    GetPathComponent(argv[2],TailPath,cli_wand->wand.name);
734    ProcessScriptOptions(cli_wand,argc,argv,2);
735  }
736  else {
737    /* Normal Command Line, assumes output file as last option */
738    ProcessCommandOptions(cli_wand,argc,argv,1, process_flags);
739  }
740  /* ------------- */
741
742Magick_Command_Cleanup:
743  /* recover original image_info from bottom of stack */
744  while (cli_wand->image_info_stack != (Stack *)NULL)
745    CLISpecialOperator(cli_wand,"}",(const char *)NULL);
746
747  /* assert we have recovered the original structures */
748  assert(cli_wand->wand.image_info == image_info);
749  assert(cli_wand->wand.exception == exception);
750
751  /* Handle metadata for ImageMagickObject COM object for Windows VBS */
752  if (metadata != (char **) NULL) {
753    const char
754      *format;
755
756    char
757      *text;
758
759    format="%w,%h,%m";   // Get this from image_info Option splaytree
760
761    text=InterpretImageProperties(image_info,cli_wand->wand.images,format,
762         exception);
763    if (text == (char *) NULL)
764      ThrowMagickException(exception,GetMagickModule(),ResourceLimitError,
765           "MemoryAllocationFailed","`%s'", GetExceptionMessage(errno));
766    else {
767      (void) ConcatenateString(&(*metadata),text);
768      text=DestroyString(text);
769    }
770  }
771
772Magick_Command_Exit:
773  /* Destroy the special CLI Wand */
774  cli_wand->wand.image_info = (ImageInfo *)NULL; /* not these */
775  cli_wand->wand.exception = (ExceptionInfo *)NULL;
776  cli_wand=DestroyMagickCLI(cli_wand);
777
778  return(IsMagickTrue(exception->severity > ErrorException));
779}
780