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