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