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