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