]> granicus.if.org Git - imagemagick/blobdiff - MagickWand/magick-cli.c
(no commit message)
[imagemagick] / MagickWand / magick-cli.c
index 51302b2ffae3f41d9e33b271f3aff0ca669553e1..955fbd21d719234b7da3ce7368b52d96ec62861d 100644 (file)
@@ -22,7 +22,7 @@
 %                               January 2012                                  %
 %                                                                             %
 %                                                                             %
-%  Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization      %
+%  Copyright 1999-2013 ImageMagick Studio LLC, a non-profit organization      %
 %  dedicated to making software imaging solutions freely available.           %
 %                                                                             %
 %  You may not use this file except in compliance with the License.  You may  %
 #include "MagickWand/studio.h"
 #include "MagickWand/MagickWand.h"
 #include "MagickWand/magick-wand-private.h"
+#include "MagickWand/wandcli.h"
+#include "MagickWand/wandcli-private.h"
 #include "MagickWand/operation.h"
-#include "MagickWand/operation-private.h"
 #include "MagickWand/magick-cli.h"
-#include "MagickCore/memory_.h"
-#include "MagickCore/string-private.h"
+#include "MagickWand/script-token.h"
 #include "MagickCore/utility-private.h"
+#include "MagickCore/exception-private.h"
 #include "MagickCore/version.h"
 \f
 /* verbose debugging,
-      1 - option type
-      2 - source of option
-      3 - mnemonic lookup  */
-#define MagickCommandDebug 0
-/*
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%                                                                             %
-%                                                                             %
-%                                                                             %
-%   G e t S c r i p t T o k e n                                               %
-%                                                                             %
-%                                                                             %
-%                                                                             %
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%
-%  GetScriptToken() is fairly general, finite state token parser. That will
-%  divide a input file stream into tokens, in a way that is as close to a
-%  UNIX shell, as is feasable.  Only shell variable, and command
-%  substitutions will not be performed.
-%
-%  Tokens are white space separated, and may be quoted, or even partially
-%  quoted by either single or double quotes, or the use of backslashes,
-%  or any mix of the three.
-%
-%  For example:    This\ is' a 'single" token"
-%
-%  A token is returned immediatally the end of token is found. That is as soon
-%  as a ending white-space or EOF condition was determined.  That is to say
-%  the file stream is parsed purely character-by-character, regardless any
-%  buffering constraints set by the system.
-%
-%  The function will return 'MagickTrue' if a valid token was found, while
-%  the token status will be set accordingally to 'OK' or 'EOF', according to
-%  the cause of the end of token.  The token may be an empty string if the
-%  input was a quoted empty string.  Other error conditions return a value of
-%  MagickFalse, indicating any token found was incomplete due to the error
-%  condition.
-%
-%  Single quotes will preserve all characters including backslashes. Double
-%  quotes will also preserve backslashes unless escaping a double quote,
-%  or another backslashes.  Other shell meta-characters are not treated as
-%  special by this tokenizer.
-%
-%  For example Quoting the quote chars:
-%              \'  "'"       \"  '"'  "\""      \\  '\'  "\\"
-%
-%  Outside quotes, backslash characters will make spaces, tabs and quotes part
-%  of a token returned. However a backslash at the end of a line (and outside
-%  quotes) will cause the newline to be completely ignored (as per the shell
-%  line continuation).
-%
-%  Comments start with a '#' character at the start of a new token, will be
-%  completely ignored upto the end of line, regardless of any backslash at the
-%  end of the line.  You can escape a comment '#', using quotes or backlsashes
-%  just as you can in a shell.
-%
-%  The format of the MagickImageCommand method is:
-%
-%     MagickBooleanType GetScriptToken(ScriptTokenInfo *token_info)
-%
-%  A description of each parameter follows:
-%
-%    o token_info    pointer to a structure holding token details
-%
+      0 - no debug lines
+      3 - show option details  (better to use -debug Command now)
+      5 - image counts (after option runs)
 */
+#define MagickCommandDebug 0
 
-/* States of the parser */
-#define IN_WHITE 0
-#define IN_TOKEN 1
-#define IN_QUOTE 2
-#define IN_COMMENT 3
-
-typedef enum {
-  TokenStatusOK = 0,
-  TokenStatusEOF,
-  TokenStatusBadQuotes,
-  TokenStatusTokenTooBig,
-  TokenStatusBinary
-} TokenStatus;
-
-typedef struct
-{
-  FILE
-    *stream;        /* the file stream we are reading from */
-
-  char
-    *token;         /* array of characters to holding details of he token */
-
-  size_t
-    length,         /* length of token char array */
-    curr_line,      /* current location in script file */
-    curr_column,
-    token_line,     /* location of the start of this token */
-    token_column;
-
-  TokenStatus
-    status;         /* Have we reached EOF? see Token Status */
-} ScriptTokenInfo;
-
-/* macro to read character from stream */
-#define GetChar(c) \
-{ \
-   c=fgetc(token_info->stream); \
-   token_info->curr_column++; \
-   if ( c == '\n' ) \
-     token_info->curr_line++, token_info->curr_column=0; \
-}
-/* macro to collect the token characters */
-#define SaveChar(c) \
-{ \
-  if ((size_t) offset >= (token_info->length-1)) \
-    { token_info->token[offset++]='\0'; \
-      token_info->status=TokenStatusTokenTooBig; \
-      return(MagickFalse); \
-    } \
-  token_info->token[offset++]=(char) (c); \
-}
-
-static MagickBooleanType GetScriptToken(ScriptTokenInfo *token_info)
-{
-
-  int
-    quote,
-    c;
-
-  int
-    state;
-
-  ssize_t
-    offset;
-
-  /* EOF - no more tokens! */
-  if (token_info->status != TokenStatusOK)
-    {
-      token_info->token[0]='\0';
-      return(MagickFalse);
-    }
-
-  state=IN_WHITE;
-  quote='\0';
-  offset=0;
-  while(1)
-  {
-    /* get character */
-    GetChar(c);
-    if (c == '\0' || c == EOF)
-      break;
-
-    /* hash comment handling */
-    if ( state == IN_COMMENT )
-      { if ( c == '\n' )
-          state=IN_WHITE;
-        continue;
-      }
-    if (c == '#' && state == IN_WHITE)
-      state=IN_COMMENT;
-    /* whitespace break character */
-    if (strchr(" \n\r\t",c) != (char *)NULL)
-      {
-        switch (state)
-        {
-          case IN_TOKEN:
-            token_info->token[offset]='\0';
-            return(MagickTrue);
-          case IN_QUOTE:
-            SaveChar(c);
-            break;
-        }
-        continue;
-      }
-    /* quote character */
-    if (strchr("'\"",c) != (char *)NULL)
-      {
-        switch (state)
-        {
-          case IN_WHITE:
-            token_info->token_line=token_info->curr_line;
-            token_info->token_column=token_info->curr_column;
-          case IN_TOKEN:
-            state=IN_QUOTE;
-            quote=c;
-            break;
-          case IN_QUOTE:
-            if (c == quote)
-              {
-                state=IN_TOKEN;
-                quote='\0';
-              }
-            else
-              SaveChar(c);
-            break;
-        }
-        continue;
-      }
-    /* escape char (preserve in quotes - unless escaping the same quote) */
-    if (c == '\\')
-      {
-        if ( state==IN_QUOTE && quote == '\'' )
-          {
-            SaveChar('\\');
-            continue;
-          }
-        GetChar(c);
-        if (c == '\0' || c == EOF)
-          {
-            SaveChar('\\');
-            break;
-          }
-        if (c == '\n')
-          switch (state)
-          {
-            case IN_COMMENT:
-              state=IN_WHITE;  /* end comment */
-            case IN_WHITE:
-            case IN_TOKEN:
-              continue;   /* line continuation (outside quotes and comment) */
-          }
-        switch (state)
-        {
-          case IN_WHITE:
-            token_info->token_line=token_info->curr_line;
-            token_info->token_column=token_info->curr_column;
-            state=IN_TOKEN;
-            break;
-          case IN_QUOTE:
-            if (c != quote && c != '\\')
-              SaveChar('\\');
-            break;
-        }
-        SaveChar(c);
-        continue;
-      }
-    /* ordinary character */
-    switch (state)
-    {
-      case IN_WHITE:
-        token_info->token_line=token_info->curr_line;
-        token_info->token_column=token_info->curr_column;
-        state=IN_TOKEN;
-      case IN_TOKEN:
-      case IN_QUOTE:
-        SaveChar(c);
-        break;
-      case IN_COMMENT:
-        break;
-    }
-  }
-  /* stream has EOF or produced a fatal error */
-  token_info->token[offset]='\0';
-  token_info->status = TokenStatusEOF;
-  if (state == IN_QUOTE)
-    token_info->status = TokenStatusBadQuotes;
-  if (c == '\0' )
-    token_info->status = TokenStatusBinary;
-  if (state == IN_TOKEN && token_info->status == TokenStatusEOF)
-    return(MagickTrue);
-  return(MagickFalse);
-}
 \f
 /*
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -330,261 +79,226 @@ static MagickBooleanType GetScriptToken(ScriptTokenInfo *token_info)
 %
 %  ProcessScriptOptions() reads options and processes options as they are
 %  found in the given file, or pipeline.  The filename to open and read
-%  options is given as the zeroth argument of the argument array given.
+%  options is given as the 'index' argument of the argument array given.
 %
-%  A script not 'return' to the command line processing, nor can they
-%  call (and return from) other scripts. At least not at this time.
+%  Other arguments following index may be read by special script options
+%  as settings (strings), images, or as operations to be processed in various
+%  ways.   How they are treated is up to the script being processed.
 %
-%  However special script options may used to read and process the other
-%  argument provided, typically those that followed a "-script" option on the
-%  command line. These extra script arguments may be interpreted as being
-%  images to read or write, settings (strings), or more options to be
-%  processed.  How they are treated is up to the script being processed.
+%  Note that a script not 'return' to the command line processing, nor can
+%  they call (and return from) other scripts. At least not at this time.
+%
+%  There are no 'ProcessOptionFlags' control flags at this time.
 %
 %  The format of the ProcessScriptOptions method is:
 %
-%    void ProcessScriptOptions(MagickCLI *cli_wand,int argc,char **argv)
+%    void ProcessScriptOptions(MagickCLI *cli_wand,const char *filename,
+%       int argc,char **argv,int index)
 %
 %  A description of each parameter follows:
 %
 %    o cli_wand: the main CLI Wand to use.
 %
-%    o argc: the number of elements in the argument vector.
+%    o filename: the filename of script to process
 %
-%    o argv: A text array containing the command line arguments.
+%    o argc: the number of elements in the argument vector. (optional)
+%
+%    o argv: A text array containing the command line arguments. (optional)
+%
+%    o index: offset of next argment in argv (script arguments) (optional)
 %
 */
-#define MagickExceptionScript(severity,tag,arg,line,col) \
-  (void) ThrowMagickException(cli_wand->wand.exception,GetMagickModule(), \
-       severity,tag, "'%s' : Line %u Column %u of script \"%s\"", \
-       arg, line, col, cli_wand->wand.name);
-
-WandExport void ProcessScriptOptions(MagickCLI *cli_wand,int argc,char **argv)
+WandExport void ProcessScriptOptions(MagickCLI *cli_wand,const char *filename,
+     int argc,char **argv,int index)
 {
-  char
-    *option,
-    *arg1,
-    *arg2;
-
-  ssize_t
-    count;
-
-  size_t
-    option_line,       /* line and column of current option */
-    option_column;
+  ScriptTokenInfo
+    *token_info;
 
   CommandOptionFlags
     option_type;
 
-  ScriptTokenInfo
-    token_info;
+  int
+    count;
 
-  MagickBooleanType
-    plus_alt_op,
-    file_opened;
+  char
+    *option,
+    *arg1,
+    *arg2;
 
-  assert(argc>0 && argv[argc-1] != (char *)NULL);
+  assert(filename != (char *)NULL ); /* at least one argument - script name */
   assert(cli_wand != (MagickCLI *) NULL);
   assert(cli_wand->signature == WandSignature);
-  if (cli_wand->wand.debug != MagickFalse)
-    (void) LogMagickEvent(WandEvent,GetMagickModule(),"%s",cli_wand->wand.name);
-
-  /* Initialize variables */
-  /* FUTURE handle file opening for '-' 'fd:N' or script filename */
-  file_opened=MagickFalse;
-  if ( LocaleCompare(argv[0],"-") == 0 )
-    {
-      CopyMagickString(cli_wand->wand.name,"stdin",MaxTextExtent);
-      token_info.stream=stdin;
-      file_opened=MagickFalse;
-    }
+  if (IfMagickTrue(cli_wand->wand.debug))
+    (void) LogMagickEvent(CommandEvent,GetMagickModule(),
+         "Processing script \"%s\"", filename);
+
+  /* open file script or stream, and set up tokenizer */
+  token_info = AcquireScriptTokenInfo(filename);
+  if (token_info == (ScriptTokenInfo *) NULL) {
+    CLIWandExceptionFile(OptionFatalError,"UnableToOpenScript",filename);
+    return;
+  }
+
+  /* define the error location string for use in exceptions
+     order of localtion format escapes: filename, line, column */
+  cli_wand->location="in \"%s\" at line %u,column %u";
+  if ( LocaleCompare("-", filename) == 0 )
+    cli_wand->filename="stdin";
   else
-    {
-      GetPathComponent(argv[0],TailPath,cli_wand->wand.name);
-      token_info.stream=fopen(argv[0], "r");
-      file_opened=MagickTrue;
-    }
+    cli_wand->filename=filename;
 
+  /* Process Options from Script */
   option = arg1 = arg2 = (char*)NULL;
-  token_info.curr_line=1;
-  token_info.curr_column=0;
-  token_info.status=TokenStatusOK;
-  token_info.length=MaxTextExtent;
-  token_info.token=(char *) AcquireQuantumMemory(MaxTextExtent,sizeof(char));
-  if (token_info.token == (char *) NULL)
-    {
-      if ( file_opened != MagickFalse )
-        fclose(token_info.stream);
-      MagickExceptionScript(ResourceLimitError,"MemoryAllocationFailed","",0,0);
-      (void) ThrowMagickException(cli_wand->wand.exception,GetMagickModule(),
-           ResourceLimitError,"MemoryAllocationFailed","script token buffer");
-      return;
+  while (1) {
+
+    { MagickBooleanType status = GetScriptToken(token_info);
+      cli_wand->line=token_info->token_line;
+      cli_wand->column=token_info->token_column;
+      if( IfMagickFalse(status) )
+        break; /* error or end of options */
     }
 
-  /* Process Options from Script */
-  while (1)
-    {
-      /* Get a option */
-      if( GetScriptToken(&token_info) == MagickFalse )
-        break;
-
-      /* option length sanity check */
-      if( strlen(token_info.token) > 40 )
-        { token_info.token[37] = '.';
-          token_info.token[38] = '.';
-          token_info.token[39] = '.';
-          token_info.token[40] = '\0';
-          MagickExceptionScript(OptionFatalError,"UnrecognizedOption",
-               token_info.token,token_info.token_line,token_info.token_column);
-          break;
-        }
+    do { /* use break to loop to exception handler and loop */
 
       /* save option details */
-      CloneString(&option,token_info.token);
-      option_line=token_info.token_line;
-      option_column=token_info.token_column;
+      CloneString(&option,token_info->token);
 
-#if MagickCommandDebug >=2
-      (void) FormatLocaleFile(stderr, "Script Option Token: %u,%u: \"%s\"\n",
-               option_line, option_column, option );
-#endif
-      /* get option type and argument count */
-      { const OptionInfo *option_info = GetCommandOptionInfo(option);
-        count=option_info->type;
-        option_type=option_info->flags;
-#if MagickCommandDebug >= 3
-        (void) FormatLocaleFile(stderr, "option \"%s\" matched \"%s\"\n",
-             option, option_info->mnemonic );
+      /* get option, its argument count, and option type */
+      cli_wand->command = GetCommandOptionInfo(option);
+      count=cli_wand->command->type;
+      option_type=(CommandOptionFlags) cli_wand->command->flags;
+#if 0
+      (void) FormatLocaleFile(stderr, "Script: %u,%u: \"%s\" matched \"%s\"\n",
+          cli_wand->line, cli_wand->line, option, cli_wand->command->mnemonic );
 #endif
-      }
 
-      /* handle a undefined option - image read? */
+      /* handle a undefined option - image read - always for "magick-script" */
       if ( option_type == UndefinedOptionFlag ||
-           (option_type & NonMagickOptionFlag) != 0 )
-        {
-#if MagickCommandDebug
-          (void) FormatLocaleFile(stderr, "Script Non-Option: \"%s\"\n", option);
+           (option_type & NonMagickOptionFlag) != 0 ) {
+#if MagickCommandDebug >= 3
+        (void) FormatLocaleFile(stderr, "Script %u,%u Non-Option: \"%s\"\n",
+                    cli_wand->line, cli_wand->line, option);
 #endif
-          if ( IsCommandOption(option) == MagickFalse)
-            {
-              /* non-option -- treat as a image read */
-              CLISpecialOperator(cli_wand,"-read",option);
-              count = 0;
-            }
-          else
-            MagickExceptionScript(OptionFatalError,"UnrecognizedOption",
-                 option,option_line,option_column);
-
-          if ( CLICatchException(cli_wand, MagickFalse) != MagickFalse )
-            break;
-
-          continue;
+        if ( IfMagickFalse(IsCommandOption(option))) {
+          /* non-option -- treat as a image read */
+          cli_wand->command=(const OptionInfo *)NULL;
+          CLIOption(cli_wand,"-read",option);
+          break; /* next option */
         }
+        CLIWandException(OptionFatalError,"UnrecognizedOption",option);
+        break; /* next option */
+      }
 
-      plus_alt_op = MagickFalse;
-      if (*option=='+') plus_alt_op = MagickTrue;
-
-      if ( count >= 1 )
-        {
-          if( GetScriptToken(&token_info) == MagickFalse )
-            {
-              MagickExceptionScript(OptionError,"MissingArgument",option,
-                 option_line,option_column);
-              break;
-            }
-          CloneString(&arg1,token_info.token);
-        }
+      if ( count >= 1 ) {
+        if( IfMagickFalse(GetScriptToken(token_info)) )
+          CLIWandException(OptionFatalError,"MissingArgument",option);
+        CloneString(&arg1,token_info->token);
+      }
       else
-        CloneString(&arg1,(*option!='+')?"true":(char *)NULL);
-
-      if ( count >= 2 )
-        {
-          if( GetScriptToken(&token_info) == MagickFalse )
-            {
-              MagickExceptionScript(OptionError,"MissingArgument",option,
-                 option_line,option_column);
-              break;
-            }
-          CloneString(&arg2,token_info.token);
-        }
+        CloneString(&arg1,(char *)NULL);
+
+      if ( count >= 2 ) {
+        if( IfMagickFalse(GetScriptToken(token_info)) )
+          CLIWandExceptionBreak(OptionFatalError,"MissingArgument",option);
+        CloneString(&arg2,token_info->token);
+      }
       else
         CloneString(&arg2,(char *)NULL);
 
-      /* handle script special options */
-      //either continue processing command line
-      // or making use of the command line options.
-      //CLICommandOptions(cli_wand,count+1,argv, MagickScriptArgsFlags);
-
-#if MagickCommandDebug
+      /*
+        Process Options
+      */
+#if MagickCommandDebug >= 3
       (void) FormatLocaleFile(stderr,
-          "Script Option: \"%s\" \tCount: %d  Flags: %04x  Args: \"%s\" \"%s\"\n",
-          option,(int) count,option_type,arg1,arg2);
+        "Script %u,%u Option: \"%s\"  Count: %d  Flags: %04x  Args: \"%s\" \"%s\"\n",
+            cli_wand->line,cli_wand->line,option,count,option_type,arg1,arg2);
 #endif
+      /* Hard Depreciated Options, no code to execute - error */
+      if ( (option_type & DeprecateOptionFlag) != 0 ) {
+        CLIWandException(OptionError,"DeprecatedOptionNoCode",option);
+        break; /* next option */
+      }
 
-      /* Process non-script specific option from file */
-      if ( (option_type & SpecialOptionFlag) != 0 )
-        {
-          if ( LocaleCompare(option,"-exit") == 0 )
-            break;
-          /* No "-script" from script at this time */
-          CLISpecialOperator(cli_wand,option,arg1);
-        }
+      /* MagickCommandGenesis() options have no place in a magick script */
+      if ( (option_type & GenesisOptionFlag) != 0 ) {
+        CLIWandException(OptionError,"InvalidUseOfOption",option);
+        break; /* next option */
+      }
 
-      if ( (option_type & SettingOptionFlags) != 0 )
-        {
-          CLISettingOptionInfo(cli_wand, option+1, arg1);
-          // FUTURE: Sync Specific Settings into Images
+      /* handle any special 'script' options */
+      if ( (option_type & SpecialOptionFlag) != 0 ) {
+        if ( LocaleCompare(option,"-exit") == 0 ) {
+          goto loop_exit; /* break out of loop - return from script */
         }
+        if ( LocaleCompare(option,"-script") == 0 ) {
+          /* FUTURE: call new script from this script - error for now */
+          CLIWandException(OptionError,"InvalidUseOfOption",option);
+          break; /* next option */
+        }
+        /* FUTURE: handle special script-argument options here */
+        /* handle any other special operators now */
+        CLIWandException(OptionError,"InvalidUseOfOption",option);
+        break; /* next option */
+      }
 
-      if ( (option_type & SimpleOperatorOptionFlag) != 0)
-        CLISimpleOperatorImages(cli_wand, plus_alt_op, option+1, arg1, arg2);
-
-      if ( (option_type & ListOperatorOptionFlag) != 0 )
-        CLIListOperatorImages(cli_wand, plus_alt_op, option+1, arg1, arg2);
+      /* Process non-specific Option */
+      CLIOption(cli_wand, option, arg1, arg2);
 
-      if ( CLICatchException(cli_wand, MagickFalse) != MagickFalse )
-        break;
-    }
+    } while (0); /* break block to next option */
 
-#if MagickCommandDebug
-  (void) FormatLocaleFile(stderr, "Script End: %d\n", token_info.status);
+#if MagickCommandDebug >= 5
+    fprintf(stderr, "Script Image Count = %ld\n",
+         GetImageListLength(cli_wand->wand.images) );
 #endif
-  /* token sanity for error report */
-  if( strlen(token_info.token) > 40 )
-    { token_info.token[37] = '.';
-      token_info.token[38] = '.';
-      token_info.token[39] = '.';
-      token_info.token[40] = '\0';
-    }
+    if ( IfMagickTrue(CLICatchException(cli_wand, MagickFalse)) )
+      break;  /* exit loop */
+  }
 
-   switch( token_info.status )
-    {
-      case TokenStatusBadQuotes:
-        MagickExceptionScript(OptionFatalError,"ScriptUnbalancedQuotes",
-             token_info.token,token_info.token_line,token_info.token_column);
-        break;
-      case TokenStatusTokenTooBig:
-        MagickExceptionScript(OptionFatalError,"ScriptTokenTooBig",
-             token_info.token,token_info.token_line,token_info.token_column);
-        break;
-      case TokenStatusBinary:
-        MagickExceptionScript(OptionFatalError,"ScriptIsBinary","",
-             token_info.curr_line,token_info.curr_column);
-        break;
-      case TokenStatusOK:
-      case TokenStatusEOF:
-        break;
-    }
+  /*
+     Loop exit - check for some tokenization error
+  */
+loop_exit:
+#if MagickCommandDebug >= 3
+  (void) FormatLocaleFile(stderr, "Script End: %d\n", token_info->status);
+#endif
+  switch( token_info->status ) {
+    case TokenStatusOK:
+    case TokenStatusEOF:
+      if (cli_wand->image_list_stack != (Stack *)NULL)
+        CLIWandException(OptionError,"UnbalancedParenthesis", "(eof)");
+      else if (cli_wand->image_info_stack != (Stack *)NULL)
+        CLIWandException(OptionError,"UnbalancedBraces", "(eof)");
+      break;
+    case TokenStatusBadQuotes:
+      /* Ensure last token has a sane length for error report */
+      if( strlen(token_info->token) > INITAL_TOKEN_LENGTH-1 ) {
+        token_info->token[INITAL_TOKEN_LENGTH-4] = '.';
+        token_info->token[INITAL_TOKEN_LENGTH-3] = '.';
+        token_info->token[INITAL_TOKEN_LENGTH-2] = '.';
+        token_info->token[INITAL_TOKEN_LENGTH-1] = '\0';
+      }
+      CLIWandException(OptionFatalError,"ScriptUnbalancedQuotes",
+           token_info->token);
+      break;
+    case TokenStatusMemoryFailed:
+      CLIWandException(OptionFatalError,"ScriptTokenMemoryFailed","");
+      break;
+    case TokenStatusBinary:
+      CLIWandException(OptionFatalError,"ScriptIsBinary","");
+      break;
+  }
+  if (IfMagickTrue(cli_wand->wand.debug))
+    (void) LogMagickEvent(CommandEvent,GetMagickModule(),
+         "Script End \"%s\"", filename);
 
-   /* Clean up */
-   if ( file_opened != MagickFalse )
-     fclose(token_info.stream);
+  /* Clean up */
+  token_info = DestroyScriptTokenInfo(token_info);
 
-   CloneString(&option,(char *)NULL);
-   CloneString(&arg1,(char *)NULL);
-   CloneString(&arg2,(char *)NULL);
+  CloneString(&option,(char *)NULL);
+  CloneString(&arg1,(char *)NULL);
+  CloneString(&arg2,(char *)NULL);
 
-   return;
+  return;
 }
 \f
 /*
@@ -599,8 +313,8 @@ WandExport void ProcessScriptOptions(MagickCLI *cli_wand,int argc,char **argv)
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 %
 %  ProcessCommandOptions() reads and processes arguments in the given
-%  command line argument array. The array does not contain the command
-%  being processed, only the options.
+%  command line argument array. The 'index' defines where in the array we
+%  should begin processing
 %
 %  The 'process_flags' can be used to control and limit option processing.
 %  For example, to only process one option, or how unknown and special options
@@ -609,8 +323,8 @@ WandExport void ProcessScriptOptions(MagickCLI *cli_wand,int argc,char **argv)
 %
 %  The format of the ProcessCommandOptions method is:
 %
-%    int ProcessCommandOptions(MagickCLI *cli_wand,int argc,char **argv,
-%           int *index, ProcessOptionFlags process_flags )
+%    int ProcessCommandOptions(MagickCLI *cli_wand,
+%           int argc,char **argv,int index)
 %
 %  A description of each parameter follows:
 %
@@ -620,32 +334,25 @@ WandExport void ProcessScriptOptions(MagickCLI *cli_wand,int argc,char **argv)
 %
 %    o argv: A text array containing the command line arguments.
 %
-%    o process_flags: What type of arguments we are allowed to process
+%    o process_flags: What type of arguments will be processed, ignored
+%                     or return errors.
+%
+%    o index: index in the argv array to start processing from
+%
+% The function returns the index ot the next option to be processed. This
+% is really only releven if process_flags contains a ProcessOneOptionOnly
+% flag.
 %
 */
-/* FUTURE: correctly identify option... CLI arg,  Script line,column  */
-#define MagickExceptionContinue(severity,tag,arg,index) \
-  (void) ThrowMagickException(cli_wand->wand.exception,GetMagickModule(),severity,tag, \
-       "'%s' : CLI Arg #%d", arg, (int) index); \
-
-#define MagickExceptionReturn(severity,tag,option,arg) \
-{ \
-  MagickExceptionContinue(severity,tag,option,arg); \
-  return; \
-}
-
-WandExport void ProcessCommandOptions(MagickCLI *cli_wand,int argc,
-     char **argv, ProcessOptionFlags process_flags )
+WandExport int ProcessCommandOptions(MagickCLI *cli_wand, int argc,
+     char **argv, int index )
 {
   const char
     *option,
     *arg1,
     *arg2;
 
-  MagickBooleanType
-    plus_alt_op;
-
-  ssize_t
+  int
     i,
     end,
     count;
@@ -653,173 +360,161 @@ WandExport void ProcessCommandOptions(MagickCLI *cli_wand,int argc,
   CommandOptionFlags
     option_type;
 
-  assert(argc>0 && argv[argc-1] != (char *)NULL);
+  assert(argc>=index); /* you may have no arguments left! */
+  assert(argv != (char **)NULL);
+  assert(argv[index] != (char *)NULL);
+  assert(argv[argc-1] != (char *)NULL);
   assert(cli_wand != (MagickCLI *) NULL);
   assert(cli_wand->signature == WandSignature);
-  if (cli_wand->wand.debug != MagickFalse)
-    (void) LogMagickEvent(WandEvent,GetMagickModule(),"%s",cli_wand->wand.name);
 
-  /*
-    Parse command-line options.
-  */
+  /* define the error location string for use in exceptions
+     order of localtion format escapes: filename, line, column */
+  cli_wand->location="at %s arg %u";
+  cli_wand->filename="CLI";
+  cli_wand->line=index;  /* note first argument we will process */
+
+  if (IfMagickTrue(cli_wand->wand.debug))
+    (void) CLILogEvent(cli_wand,CommandEvent,GetMagickModule(),
+         "- Starting (\"%s\")", argv[index]);
+
   end = argc;
-  if ( ( process_flags & ProcessOutputFile ) != 0 )
-    end--;
-  for (i=0; i < end; i += count +1)
-    {
-#if MagickCommandDebug >= 2
-      (void) FormatLocaleFile(stderr, "index= %d  option="%s\"\n", i, argv[i]);
-#endif
-      /* Finished processing one option? */
-      if ( ( process_flags & ProcessOneOptionOnly ) != 0 && i != 0 )
-        return;
+  if ( (cli_wand->process_flags & ProcessImplictWrite) != 0 )
+    end--; /* the last arument is an implied write, do not process directly */
 
-      option=argv[i];
-      plus_alt_op = MagickFalse;
-      arg1=(char *)NULL;
-      arg2=(char *)NULL;
+  for (i=index; i < end; i += count +1) {
+    /* Finished processing one option? */
+    if ( (cli_wand->process_flags & ProcessOneOptionOnly) != 0 && i != index )
+      return(i);
 
+    do { /* use break to loop to exception handler and loop */
 
-      { const OptionInfo *option_info = GetCommandOptionInfo(argv[i]);
-        count=option_info->type;
-        option_type=option_info->flags;
-#if MagickCommandDebug >= 3
-        (void) FormatLocaleFile(stderr, "option \"%s\" matched \"%s\"\n",
-             argv[i], option_info->mnemonic );
+      option=argv[i];
+      cli_wand->line=i;  /* note the argument for this option */
+
+      /* get option, its argument count, and option type */
+      cli_wand->command = GetCommandOptionInfo(argv[i]);
+      count=cli_wand->command->type;
+      option_type=(CommandOptionFlags) cli_wand->command->flags;
+#if 0
+      (void) FormatLocaleFile(stderr, "CLI %d: \"%s\" matched \"%s\"\n",
+            i, argv[i], cli_wand->command->mnemonic );
 #endif
-      }
 
       if ( option_type == UndefinedOptionFlag ||
-           (option_type & NonMagickOptionFlag) != 0 )
-        {
-#if MagickCommandDebug
-          (void) FormatLocaleFile(stderr, "CLI Non-Option: \"%s\"\n", option);
+           (option_type & NonMagickOptionFlag) != 0 ) {
+#if MagickCommandDebug >= 3
+        (void) FormatLocaleFile(stderr, "CLI arg %d Non-Option: \"%s\"\n",
+             i, option);
 #endif
-          if ( ( IsCommandOption(option) == MagickFalse ) &&
-               ( (process_flags & ProcessNonOptionImageRead) != 0 ) )
-            {
-              /* non-option -- treat as a image read */
-              CLISpecialOperator(cli_wand,"-read",option);
-              count = 0;
-            }
-          else if ( (process_flags & ProcessUnknownOptionError) != 0 )
-            MagickExceptionReturn(OptionFatalError,"UnrecognizedOption",
-                 option,i);
-
-          if ( CLICatchException(cli_wand, MagickFalse) != MagickFalse )
-            break;
-
-          continue;
+        if ( IfMagickFalse(IsCommandOption(option)) ) {
+          if ( (cli_wand->process_flags & ProcessImplictRead) != 0 ) {
+            /* non-option -- treat as a image read */
+            cli_wand->command=(const OptionInfo *)NULL;
+            CLIOption(cli_wand,"-read",option);
+            break; /* next option */
+          }
         }
+        CLIWandException(OptionFatalError,"UnrecognizedOption",option);
+        break; /* next option */
+      }
 
+      if ( ((option_type & SpecialOptionFlag) != 0 ) &&
+           ((cli_wand->process_flags & ProcessScriptOption) != 0) &&
+           (LocaleCompare(option,"-script") == 0) ) {
+        /* Call Script from CLI, with a filename as a zeroth argument.
+           NOTE: -script may need to use the 'implict write filename' argument
+           so it must be handled specially to prevent a 'missing argument' error.
+        */
+        if ( (i+count) >= argc )
+          CLIWandException(OptionFatalError,"MissingArgument",option);
+        ProcessScriptOptions(cli_wand,argv[i+1],argc,argv,i+count);
+        return(argc);  /* Script does not return to CLI -- Yet */
+                       /* FUTURE: when it does, their may be no write arg! */
+      }
 
-      if ( (option_type & DeprecateOptionFlag) != 0 )
-        {
-          MagickExceptionContinue(OptionWarning,"DeprecatedOption",option,i);
-          if ( CLICatchException(cli_wand, MagickFalse) != MagickFalse )
-            break;
-          /* FALLTHRU - continue processing */
-        }
-
-      if ((i+count) >= end )
-        {
-          MagickExceptionReturn(OptionError,"MissingArgument",option,i);
-          if ( CLICatchException(cli_wand, MagickFalse) != MagickFalse )
-            break;
-          continue; /* unable to proceed */
-        }
+      if ((i+count) >= end ) {
+        CLIWandException(OptionFatalError,"MissingArgument",option);
+        if ( CLICatchException(cli_wand, MagickFalse) != MagickFalse )
+          return(end);
+        break; /* next option - not that their is any! */
+      }
 
-      if (*option=='+') plus_alt_op = MagickTrue;
-      if (*option!='+') arg1 = "true";
-      if ( count >= 1 ) arg1 = argv[i+1];
-      if ( count >= 2 ) arg2 = argv[i+2];
+      arg1 = ( count >= 1 ) ? argv[i+1] : (char *)NULL;
+      arg2 = ( count >= 2 ) ? argv[i+2] : (char *)NULL;
 
-#if MagickCommandDebug
+      /*
+        Process Known Options
+      */
+#if MagickCommandDebug >= 3
       (void) FormatLocaleFile(stderr,
-          "CLI Option: \"%s\" \tCount: %d  Flags: %04x  Args: \"%s\" \"%s\"\n",
-          option,(int) count,option_type,arg1,arg2);
+        "CLI arg %u Option: \"%s\"  Count: %d  Flags: %04x  Args: \"%s\" \"%s\"\n",
+            i,option,count,option_type,arg1,arg2);
 #endif
+      /* ignore 'genesis options' in command line args */
+      if ( (option_type & GenesisOptionFlag) != 0 )
+        break; /* next option */
+
+      /* Handle any special options for CLI (-script handled above) */
+      if ( (option_type & SpecialOptionFlag) != 0 ) {
+        if ( (cli_wand->process_flags & ProcessExitOption) != 0
+             && LocaleCompare(option,"-exit") == 0 )
+          return(i+count);
+        break; /* next option */
+      }
 
-      if ( (option_type & SpecialOptionFlag) != 0 )
-        {
-          if ( ( process_flags & ProcessExitOption ) != 0
-               && LocaleCompare(option,"-exit") == 0 )
-            return;
-          if ( ( process_flags & ProcessScriptOption ) != 0
-               && LocaleCompare(option,"-script") == 0)
-            {
-              // Unbalanced Parenthesis if stack not empty
-              // Call Script, with a filename as a zeroth argument
-              ProcessScriptOptions(cli_wand,argc-(i+1),argv+(i+1));
-              return;
-            }
-          CLISpecialOperator(cli_wand,option,arg1);
-        }
-
-
-      if ( (option_type & SettingOptionFlags) != 0 )
-        {
-          CLISettingOptionInfo(cli_wand, option+1, arg1);
-          // FUTURE: Sync Specific Settings into Images
-        }
-
-      if ( (option_type & SimpleOperatorOptionFlag) != 0)
-        CLISimpleOperatorImages(cli_wand, plus_alt_op, option+1, arg1, arg2);
-
-      if ( (option_type & ListOperatorOptionFlag) != 0 )
-        CLIListOperatorImages(cli_wand, plus_alt_op, option+1, arg1, arg2);
+      /* Process standard image option */
+      CLIOption(cli_wand, option, arg1, arg2);
 
-      if ( CLICatchException(cli_wand, MagickFalse) != MagickFalse )
-        break;
+    } while (0); /* break block to next option */
 
-    }
+#if MagickCommandDebug >= 5
+    (void) FormatLocaleFile(stderr, "CLI-post Image Count = %ld\n",
+         (long) GetImageListLength(cli_wand->wand.images) );
+#endif
+    if ( CLICatchException(cli_wand, MagickFalse) != MagickFalse )
+      return(i+count);
+  }
+  assert(i==end);
 
-  if ( CLICatchException(cli_wand, MagickFalse) != MagickFalse )
-    return;
+  if ( (cli_wand->process_flags & ProcessImplictWrite) == 0 )
+    return(end); /* no implied write -- just return to caller */
 
-  if ( ( process_flags & ProcessOutputFile ) == 0 )
-    return;
-  assert(end==argc-1);
+  assert(end==argc-1); /* end should not include last argument */
 
   /*
      Implicit Write of images to final CLI argument
   */
   option=argv[i];
+  cli_wand->line=i;
 
-#if MagickCommandDebug
-  (void) FormatLocaleFile(stderr, "CLI Output: \"%s\"\n", option );
-#endif
+  /* check that stacks are empty - or cause exception */
+  if (cli_wand->image_list_stack != (Stack *)NULL)
+    CLIWandException(OptionError,"UnbalancedParenthesis", "(end of cli)");
+  else if (cli_wand->image_info_stack != (Stack *)NULL)
+    CLIWandException(OptionError,"UnbalancedBraces", "(end of cli)");
+  if ( CLICatchException(cli_wand, MagickFalse) != MagickFalse )
+    return(argc);
 
-  // if stacks are not empty
-  //  ThrowConvertException(OptionError,"UnbalancedParenthesis",option,i);
+#if MagickCommandDebug >= 3
+  (void) FormatLocaleFile(stderr,"CLI arg %d Write File: \"%s\"\n",i,option);
+#endif
 
-  /* This is a valid 'do no write' option for a CLI */
+  /* Valid 'do no write' replacement option (instead of "null:") */
   if (LocaleCompare(option,"-exit") == 0 )
-    return;  /* just exit, no image write */
-
-  /* If there is an option -- produce an error */
-  if (IsCommandOption(option) != MagickFalse)
-    /* FUTURE: Better Error - Output Filename not Found */
-    MagickExceptionReturn(OptionError,"MissingOutputFilename",option,i);
-
-  /* If no images in MagickCLI */
-  if ( cli_wand->wand.images == (Image *) NULL )
-    {
-      /* a "null:" output coder with no images is not an error! */
-      if ( LocaleCompare(option,"null:") == 0 )
-        return;
-      MagickExceptionReturn(OptionError,"NoImagesForFinalWrite",option,i);
-    }
+    return(argc);  /* just exit, no image write */
+
+  /* If filename looks like an option,
+     Or the common 'end of line' error of a single space.
+     -- produce an error */
+  if (IfMagickTrue(IsCommandOption(option)) ||
+      (option[0] == ' ' && option[1] == '\0') ) {
+    CLIWandException(OptionError,"MissingOutputFilename",option);
+    return(argc);
+  }
 
-#if 0
-  WandListOperatorImages(cli_wand,MagickFalse,"write",option,(const char *)NULL);
-#else
-  (void) SyncImagesSettings(cli_wand->wand.image_info,cli_wand->wand.images,
-       cli_wand->wand.exception);
-  (void) WriteImages(cli_wand->wand.image_info,cli_wand->wand.images,option,
-       cli_wand->wand.exception);
-#endif
-  return;
+  cli_wand->command=(const OptionInfo *)NULL;
+  CLIOption(cli_wand,"-write",option);
+  return(argc);
 }
 \f
 /*
@@ -842,41 +537,82 @@ WandExport void ProcessCommandOptions(MagickCLI *cli_wand,int argc,
 %
 %  The format of the MagickImageCommand method is:
 %
-%      MagickBooleanType MagickImageCommand(ImageInfo *image_info,
-%           int argc, char **argv, char **metadata, ExceptionInfo *exception)
+%      MagickBooleanType MagickImageCommand(ImageInfo *image_info,int argc,
+%        char **argv,char **metadata,ExceptionInfo *exception)
 %
 %  A description of each parameter follows:
 %
 %    o image_info: the starting image_info structure
-%         (for compatibilty with MagickCommandGenisis())
+%      (for compatibilty with MagickCommandGenisis())
 %
 %    o argc: the number of elements in the argument vector.
 %
 %    o argv: A text array containing the command line arguments.
 %
-%    o metadata: any metadata is returned here.
-%         (for compatibilty with MagickCommandGenisis())
+%    o metadata: any metadata (for VBS) is returned here.
+%      (for compatibilty with MagickCommandGenisis())
 %
 %    o exception: return any errors or warnings in this structure.
 %
 */
 
-static MagickBooleanType MagickUsage(void)
+static void MagickUsage(MagickBooleanType verbose)
 {
-  printf("Version: %s\n",GetMagickVersion((size_t *) NULL));
-  printf("Copyright: %s\n",GetMagickCopyright());
-  printf("Features: %s\n\n",GetMagickFeatures());
-  printf("\n");
-
-  printf("Usage: %s [(options|images) ...] output_image\n", GetClientName());
-  printf("       %s -script filename [script args...]\n", GetClientName());
-  printf("       ... | %s -script - | ...\n", GetClientName());
-  printf("\n");
-
-  printf("  For more information on usage, options, examples, and technqiues\n");
-  printf("  see the ImageMagick website at\n    %s\n", MagickAuthoritativeURL);
-  printf("  Or the web pages in ImageMagick Sources\n");
-  return(MagickFalse);
+  const char
+    *name;
+
+  size_t
+    len;
+
+  name=GetClientName();
+  len=strlen(name);
+
+  if (len>=7 && LocaleCompare("convert",name+len-7) == 0) {
+    /* convert usage */
+    (void) FormatLocaleFile(stdout,
+       "Usage: %s [ {option} | {image} ... ] {output_image}\n",name);
+    (void) FormatLocaleFile(stdout,
+       "       %s -help | -version | -usage | -list {option}\n\n",name);
+    return;
+  }
+  else if (len>=6 && LocaleCompare("script",name+len-6) == 0) {
+    /* magick-script usage */
+    (void) FormatLocaleFile(stdout,
+      "Usage: %s {filename} [ {script_args} ... ]\n",name);
+  }
+  else {
+    /* magick usage */
+    (void) FormatLocaleFile(stdout,
+       "Usage: %s [ {option} | {image} ... ] {output_image}\n",name);
+    (void) FormatLocaleFile(stdout,
+       "       %s [ {option} | {image} ... ] -script {filename} [ {script_args} ...]\n",
+       name);
+  }
+  (void) FormatLocaleFile(stdout,
+    "       %s -help | -version | -usage | -list {option}\n\n",name);
+
+  if (IfMagickFalse(verbose))
+    return;
+
+  (void) FormatLocaleFile(stdout,"%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
+    "All options are performed in a strict 'as you see them' order\n",
+    "You must read-in images before you can operate on them.\n",
+    "\n",
+    "Magick Script files can use any of the following forms...\n",
+    "     #!/path/to/magick -script\n",
+    "or\n",
+    "     #!/bin/sh\n",
+    "     :; exec magick -script \"$0\" \"$@\"; exit 10\n",
+    "     # Magick script from here...\n",
+    "or\n",
+    "     #!/usr/bin/env  magick-script\n",
+    "The latter two forms do not require the path to the command hard coded.\n",
+    "Note: \"magick-script\" needs to be linked to the \"magick\" command.\n",
+    "\n",
+    "For more information on usage, options, examples, and techniques\n",
+    "see the ImageMagick website at    ", MagickAuthoritativeURL);
+
+  return;
 }
 
 /*
@@ -889,19 +625,8 @@ static MagickBooleanType MagickUsage(void)
    This is much like the UNIX "cat" command, but for both UNIX and Windows,
    however the last argument provides the output filename.
 */
-#define ThrowFileException(exception,severity,tag,context) \
-{ \
-  char \
-    *message; \
- \
-  message=GetExceptionMessage(errno); \
-  (void) ThrowMagickException(exception,GetMagickModule(),severity, \
-    tag == (const char *) NULL ? "unknown" : tag,"`%s': %s",context,message); \
-  message=DestroyString(message); \
-}
-
 static MagickBooleanType ConcatenateImages(int argc,char **argv,
-  ExceptionInfo *exception)
+     ExceptionInfo *exception )
 {
   FILE
     *input,
@@ -913,18 +638,24 @@ static MagickBooleanType ConcatenateImages(int argc,char **argv,
   register ssize_t
     i;
 
+  if (IfMagickFalse(  ExpandFilenames(&argc,&argv)  ))
+    ThrowFileException(exception,ResourceLimitError,"MemoryAllocationFailed",
+         GetExceptionMessage(errno));
+
   output=fopen_utf8(argv[argc-1],"wb");
-  if (output == (FILE *) NULL)
-    {
-      ThrowFileException(exception,FileOpenError,"UnableToOpenFile",
-        argv[argc-1]);
-      return(MagickFalse);
-    }
-  for (i=2; i < (ssize_t) (argc-1); i++)
-  {
+  if (output == (FILE *) NULL) {
+    ThrowFileException(exception,FileOpenError,"UnableToOpenFile",argv[argc-1]);
+    return(MagickFalse);
+  }
+  for (i=2; i < (ssize_t) (argc-1); i++) {
+#if 0
+    fprintf(stderr, "DEBUG: Concatenate Image: \"%s\"\n", argv[i]);
+#endif
     input=fopen_utf8(argv[i],"rb");
-    if (input == (FILE *) NULL)
-      ThrowFileException(exception,FileOpenError,"UnableToOpenFile",argv[i]);
+    if (input == (FILE *) NULL) {
+        ThrowFileException(exception,FileOpenError,"UnableToOpenFile",argv[i]);
+        continue;
+      }
     for (c=fgetc(input); c != EOF; c=fgetc(input))
       (void) fputc((char) c,output);
     (void) fclose(input);
@@ -940,99 +671,162 @@ WandExport MagickBooleanType MagickImageCommand(ImageInfo *image_info,
   MagickCLI
     *cli_wand;
 
-  const char
-    *option;
-
-  /* Handle special single use options */
-  if (argc == 2)
-    {
-      option=argv[1];
-      if ((LocaleCompare("-version",option+1) == 0) ||
-          (LocaleCompare("--version",option+1) == 0) )
-        {
-          (void) FormatLocaleFile(stdout,"Version: %s\n",
-            GetMagickVersion((size_t *) NULL));
-          (void) FormatLocaleFile(stdout,"Copyright: %s\n",
-            GetMagickCopyright());
-          (void) FormatLocaleFile(stdout,"Features: %s\n\n",
-            GetMagickFeatures());
-          return(MagickFalse);
-        }
-    }
-  /* The "magick" command must have at least two arguments */
-  if (argc < 3)
-    return(MagickUsage());
-  ReadCommandlLine(argc,&argv);
+  size_t
+    len;
 
-#if 0
-  /* FUTURE: This does not make sense!  Remove it.
-     Only a 'image read' needs to expand file name glob patterns
-  */
-  status=ExpandFilenames(&argc,&argv);
-  if (status == MagickFalse)
-    ThrowConvertException(ResourceLimitError,"MemoryAllocationFailed",
-      GetExceptionMessage(errno));
-#endif
+  assert(image_info != (ImageInfo *)NULL);
 
-  /* Special option (hidden) for delegate usage - no wand needed */
-  if (LocaleCompare("-concatenate",argv[1]) == 0)
-    return(ConcatenateImages(argc,argv,exception));
+  /* For specific OS command line requirements */
+  ReadCommandlLine(argc,&argv);
 
   /* Initialize special "CLI Wand" to hold images and settings (empty) */
-  /* FUTURE: add this to 'operations.c' */
   cli_wand=AcquireMagickCLI(image_info,exception);
+  cli_wand->location="Initializing";
+  cli_wand->filename=argv[0];
+  cli_wand->line=1;
 
-  if (LocaleCompare("-script",argv[1]) == 0)
-    {
-      /* Start processing directly from script, no pre-script options
-        Replace wand command name with script name
-      */
-      GetPathComponent(argv[2],TailPath,cli_wand->wand.name);
-      ProcessScriptOptions(cli_wand,argc-2,argv+2);
+  if (IfMagickTrue(cli_wand->wand.debug))
+    (void) CLILogEvent(cli_wand,CommandEvent,GetMagickModule(),
+         "\"%s\"",argv[0]);
+
+
+  GetPathComponent(argv[0],TailPath,cli_wand->wand.name);
+  SetClientName(cli_wand->wand.name);
+  ConcatenateMagickString(cli_wand->wand.name,"-CLI",MaxTextExtent);
+
+  len=strlen(argv[0]);  /* precaution */
+
+  /* "convert" command - give a "depreciation" warning" */
+  if (len>=7 && LocaleCompare("convert",argv[0]+len-7) == 0) {
+    cli_wand->process_flags = ConvertCommandOptionFlags;
+    (void) FormatLocaleFile(stderr,"WARNING: %s\n",
+             "The convert is depreciated in IMv7, use \"magick\"\n");
+  }
+
+  /* Special Case:  If command name ends with "script" implied "-script" */
+  if (len>=6 && LocaleCompare("script",argv[0]+len-6) == 0) {
+    if (argc >= 2 && (  (*(argv[1]) != '-') || (strlen(argv[1]) == 1) )) {
+      GetPathComponent(argv[1],TailPath,cli_wand->wand.name);
+      ProcessScriptOptions(cli_wand,argv[1],argc,argv,2);
+      goto Magick_Command_Cleanup;
     }
-  else if (LocaleCompare("-list",argv[1]) == 0)
-    /* Special option, list information and exit
-       FUTURE: this should be a MagickCore option, no wand is actually needed
-    */
+  }
 
-    CLISpecialOperator(cli_wand, argv[1]+1, argv[2]);
-  else
-    /* Processing Command line, assuming output file as last option */
-    ProcessCommandOptions(cli_wand,argc-1,argv+1,MagickCommandOptionFlags);
+  /* Special Case: Version Information and Abort */
+  if (argc == 2) {
+    if ((LocaleCompare("-version",argv[1]) == 0)   || /* GNU standard option */
+        (LocaleCompare("--version",argv[1]) == 0) ) { /* just version */
+      CLIOption(cli_wand, "-version");
+      goto Magick_Command_Exit;
+    }
+    if ((LocaleCompare("-help",argv[1]) == 0)   || /* GNU standard option */
+        (LocaleCompare("--help",argv[1]) == 0) ) { /* just a brief summary */
+      if (IfMagickTrue(cli_wand->wand.debug))
+        (void) CLILogEvent(cli_wand,CommandEvent,GetMagickModule(),
+            "- Special Option \"%s\"", argv[1]);
+      MagickUsage(MagickFalse);
+      goto Magick_Command_Exit;
+    }
+    if (LocaleCompare("-usage",argv[1]) == 0) {   /* both version & usage */
+      if (IfMagickTrue(cli_wand->wand.debug))
+        (void) CLILogEvent(cli_wand,CommandEvent,GetMagickModule(),
+            "- Special Option \"%s\"", argv[1]);
+      CLIOption(cli_wand, "-version" );
+      MagickUsage(MagickTrue);
+      goto Magick_Command_Exit;
+    }
+  }
+
+  /* not enough arguments -- including -help */
+  if (argc < 3) {
+    (void) FormatLocaleFile(stderr,
+       "Error: Invalid argument or not enough arguments\n\n");
+    MagickUsage(MagickFalse);
+    goto Magick_Command_Exit;
+  }
+
+  /* Special "concatenate option (hidden) for delegate usage */
+  if (LocaleCompare("-concatenate",argv[1]) == 0) {
+    if (IfMagickTrue(cli_wand->wand.debug))
+        (void) CLILogEvent(cli_wand,CommandEvent,GetMagickModule(),
+            "- Special Option \"%s\"", argv[1]);
+    ConcatenateImages(argc,argv,exception);
+    goto Magick_Command_Exit;
+  }
+
+  /* List Information and Abort */
+  if (argc == 3 && LocaleCompare("-list",argv[1]) == 0) {
+    CLIOption(cli_wand, argv[1], argv[2]);
+    goto Magick_Command_Exit;
+  }
+
+  /* ------------- */
+  /* The Main Call */
 
-  /* recover original image_info - check we get the right image_info */
+  if (LocaleCompare("-script",argv[1]) == 0) {
+    /* Start processing directly from script, no pre-script options
+       Replace wand command name with script name
+       First argument in the argv array is the script name to read.
+    */
+    GetPathComponent(argv[2],TailPath,cli_wand->wand.name);
+    ProcessScriptOptions(cli_wand,argv[2],argc,argv,3);
+  }
+  else {
+    /* Normal Command Line, assumes output file as last option */
+    ProcessCommandOptions(cli_wand,argc,argv,1);
+  }
+  /* ------------- */
+
+Magick_Command_Cleanup:
+  cli_wand->location="Cleanup";
+  cli_wand->filename=argv[0];
+  if (IfMagickTrue(cli_wand->wand.debug))
+    (void) CLILogEvent(cli_wand,CommandEvent,GetMagickModule(),
+         "\"%s\"",argv[0]);
+
+  /* recover original image_info and clean up stacks
+     FUTURE: "-reset stacks" option  */
+  while (cli_wand->image_list_stack != (Stack *)NULL)
+    CLIOption(cli_wand,")");
   while (cli_wand->image_info_stack != (Stack *)NULL)
-    CLISpecialOperator(cli_wand,"}",(const char *)NULL);
+    CLIOption(cli_wand,"}");
 
+  /* assert we have recovered the original structures */
   assert(cli_wand->wand.image_info == image_info);
   assert(cli_wand->wand.exception == exception);
 
   /* Handle metadata for ImageMagickObject COM object for Windows VBS */
-  if (metadata != (char **) NULL)
-    {
-      const char
-        *format;
-
-      char
-        *text;
+  if (metadata != (char **) NULL) {
+    const char
+      *format;
+
+    char
+      *text;
+
+    format="%w,%h,%m";   // Get this from image_info Option splaytree
+
+    text=InterpretImageProperties(image_info,cli_wand->wand.images,format,
+      exception);
+    if (text == (char *) NULL)
+      ThrowMagickException(exception,GetMagickModule(),ResourceLimitError,
+        "MemoryAllocationFailed","`%s'", GetExceptionMessage(errno));
+    else {
+      (void) ConcatenateString(&(*metadata),text);
+      text=DestroyString(text);
+    }
+  }
 
-      format="%w,%h,%m";   // Get this from image_info Option splaytree
+Magick_Command_Exit:
+  cli_wand->location="Exiting";
+  cli_wand->filename=argv[0];
+  if (IfMagickTrue(cli_wand->wand.debug))
+    (void) CLILogEvent(cli_wand,CommandEvent,GetMagickModule(),
+         "\"%s\"",argv[0]);
 
-      text=InterpretImageProperties(image_info,cli_wand->wand.images,format,
-           exception);
-      if (text == (char *) NULL)
-        ThrowMagickException(exception,GetMagickModule(),ResourceLimitError,
-             "MemoryAllocationFailed","`%s'", GetExceptionMessage(errno));
-      else
-        {
-          (void) ConcatenateString(&(*metadata),text);
-          text=DestroyString(text);
-        }
-    }
   /* Destroy the special CLI Wand */
   cli_wand->wand.image_info = (ImageInfo *)NULL; /* not these */
   cli_wand->wand.exception = (ExceptionInfo *)NULL;
   cli_wand=DestroyMagickCLI(cli_wand);
 
-  return((exception->severity > ErrorException) ? MagickFalse : MagickTrue);
+  return(IsMagickTrue(exception->severity > ErrorException));
 }