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