]> granicus.if.org Git - imagemagick/commitdiff
Separate ScriptToken from magick-cli plus my test programs
authoranthony <anthony@git.imagemagick.org>
Sat, 3 Mar 2012 02:31:18 +0000 (02:31 +0000)
committeranthony <anthony@git.imagemagick.org>
Sat, 3 Mar 2012 02:31:18 +0000 (02:31 +0000)
MagickWand/Makefile.am
MagickWand/magick-cli.c
MagickWand/operation-private.h
MagickWand/operation.c
MagickWand/script-token-test-data.txt [new file with mode: 0644]
MagickWand/script-token-test.c [new file with mode: 0644]
MagickWand/script-token-test.sh [new file with mode: 0755]
MagickWand/script-token.c [new file with mode: 0644]
MagickWand/script-token.h [new file with mode: 0644]
config/english.xml
config/francais.xml

index c5ac435bfc7108d45ca29053a7c36a19ae324cc5..84aedef86194c09344988dc5125f2a5cf31c3ddb 100644 (file)
@@ -39,6 +39,8 @@ MAGICKWAND_SOURCES = \
        MagickWand/identify.h \
        MagickWand/import.c \
        MagickWand/import.h \
+       MagickWand/magick-cli.c \
+       MagickWand/magick-cli.h \
        MagickWand/magick-image.c \
        MagickWand/magick-image.h \
        MagickWand/magick-property.c \
@@ -61,6 +63,8 @@ MAGICKWAND_SOURCES = \
        MagickWand/pixel-wand.c \
        MagickWand/pixel-wand.h \
        MagickWand/pixel-wand-private.h \
+       MagickWand/script-token.c \
+       MagickWand/script-token.h \
        MagickWand/stream.c \
        MagickWand/stream.h \
        MagickWand/studio.h \
@@ -98,6 +102,7 @@ MAGICKWAND_NOINST_HDRS = \
        MagickWand/magick-wand-private.h \
        MagickWand/operation-private.h \
        MagickWand/pixel-wand-private.h \
+       MagickWand/script-token.h \
        MagickWand/studio.h \
        MagickWand/wand.h
 
index 51302b2ffae3f41d9e33b271f3aff0ca669553e1..7c69efee08c1fd82b3be316a1c72af871c8edb23 100644 (file)
@@ -52,8 +52,7 @@
 #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/version.h"
 \f
       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
-%
-*/
-
-/* 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) \
+#define ThrowFileException(exception,severity,tag,context) \
 { \
-  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);
+    char \
+      *message; \
+   \
+    message=GetExceptionMessage(errno); \
+    (void) ThrowMagickException(exception,GetMagickModule(),severity, \
+             tag == (const char *) NULL ? "unknown" : tag,"`%s': %s",context,message); \
+    message=DestroyString(message); \
 }
 \f
 /*
@@ -361,13 +117,8 @@ static MagickBooleanType GetScriptToken(ScriptTokenInfo *token_info)
 
 WandExport void ProcessScriptOptions(MagickCLI *cli_wand,int argc,char **argv)
 {
-  char
-    *option,
-    *arg1,
-    *arg2;
-
-  ssize_t
-    count;
+  ScriptTokenInfo
+    *token_info;
 
   size_t
     option_line,       /* line and column of current option */
@@ -376,12 +127,17 @@ WandExport void ProcessScriptOptions(MagickCLI *cli_wand,int argc,char **argv)
   CommandOptionFlags
     option_type;
 
-  ScriptTokenInfo
-    token_info;
+  ssize_t
+    count;
 
   MagickBooleanType
-    plus_alt_op,
-    file_opened;
+    plus_alt_op;
+
+  char
+    *option,
+    *arg1,
+    *arg2;
+
 
   assert(argc>0 && argv[argc-1] != (char *)NULL);
   assert(cli_wand != (MagickCLI *) NULL);
@@ -389,202 +145,170 @@ WandExport void ProcessScriptOptions(MagickCLI *cli_wand,int argc,char **argv)
   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;
-    }
-  else
-    {
-      GetPathComponent(argv[0],TailPath,cli_wand->wand.name);
-      token_info.stream=fopen(argv[0], "r");
-      file_opened=MagickTrue;
-    }
+
+  token_info = AcquireScriptTokenInfo(argv[0]);
+  if (token_info->token == (char *) NULL) {
+    ThrowFileException(cli_wand->wand.exception,OptionError,
+               "UnableToOpenScript",argv[0]);
+    return;
+  }
 
   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;
-    }
 
   /* Process Options from Script */
-  while (1)
-    {
-      /* Get a option */
-      if( GetScriptToken(&token_info) == MagickFalse )
-        break;
+  while (1) {
 
-      /* 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;
-        }
+    /* Get a option */
+    if( GetScriptToken(token_info) == MagickFalse )
+      break;
 
-      /* save option details */
-      CloneString(&option,token_info.token);
-      option_line=token_info.token_line;
-      option_column=token_info.token_column;
+    /* Sanity check: option is larger than what should be posible */
+    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';
+      MagickExceptionScript(OptionFatalError,"UnrecognizedOption",
+           token_info->token,token_info->token_line,token_info->token_column);
+      break;
+  }
 
+    /* save option details */
+    CloneString(&option,token_info->token);
+    option_line=token_info->token_line;
+    option_column=token_info->token_column;
 #if MagickCommandDebug >=2
-      (void) FormatLocaleFile(stderr, "Script Option Token: %u,%u: \"%s\"\n",
-               option_line, option_column, option );
+    (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;
+
+    { /* 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 );
+      (void) FormatLocaleFile(stderr, "option \"%s\" matched \"%s\"\n",
+           option, option_info->mnemonic );
 #endif
-      }
+    }
 
       /* handle a undefined option - image read? */
-      if ( option_type == UndefinedOptionFlag ||
-           (option_type & NonMagickOptionFlag) != 0 )
-        {
+    if ( option_type == UndefinedOptionFlag ||
+         (option_type & NonMagickOptionFlag) != 0 ) {
 #if MagickCommandDebug
-          (void) FormatLocaleFile(stderr, "Script Non-Option: \"%s\"\n", option);
+        (void) FormatLocaleFile(stderr, "Script Non-Option: \"%s\"\n", 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;
+        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);
 
-          continue;
-        }
+        if ( CLICatchException(cli_wand, MagickFalse) != MagickFalse )
+          break;
+        continue;
+      }
 
-      plus_alt_op = MagickFalse;
-      if (*option=='+') plus_alt_op = MagickTrue;
+    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);
-        }
-      else
-        CloneString(&arg1,(*option!='+')?"true":(char *)NULL);
+    if ( count >= 1 ) {
+        if( GetScriptToken(token_info) == MagickFalse ) {
+            MagickExceptionScript(OptionError,"MissingArgument",option,
+               option_line,option_column);
+            break;
+          }
+        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);
-        }
-      else
-        CloneString(&arg2,(char *)NULL);
+    if ( count >= 2 ) {
+        if( GetScriptToken(token_info) == MagickFalse ) {
+            MagickExceptionScript(OptionError,"MissingArgument",option,
+               option_line,option_column);
+            break;
+          }
+        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);
+    /* 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
-      (void) FormatLocaleFile(stderr,
-          "Script Option: \"%s\" \tCount: %d  Flags: %04x  Args: \"%s\" \"%s\"\n",
-          option,(int) count,option_type,arg1,arg2);
+    (void) FormatLocaleFile(stderr,
+        "Script Option: \"%s\" \tCount: %d  Flags: %04x  Args: \"%s\" \"%s\"\n",
+        option,(int) count,option_type,arg1,arg2);
 #endif
 
-      /* 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);
-        }
+    /* 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);
+      }
 
-      if ( (option_type & SettingOptionFlags) != 0 )
-        {
-          CLISettingOptionInfo(cli_wand, option+1, arg1);
-          // FUTURE: Sync Specific Settings into Images
-        }
+    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 & 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);
+    if ( (option_type & ListOperatorOptionFlag) != 0 )
+      CLIListOperatorImages(cli_wand, plus_alt_op, option+1, arg1, arg2);
 
-      if ( CLICatchException(cli_wand, MagickFalse) != MagickFalse )
-        break;
-    }
+    if ( CLICatchException(cli_wand, MagickFalse) != MagickFalse )
+      break;
+  }
 
 #if MagickCommandDebug
-  (void) FormatLocaleFile(stderr, "Script End: %d\n", token_info.status);
+  (void) FormatLocaleFile(stderr, "Script End: %d\n", token_info->status);
 #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';
-    }
-
-   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;
-    }
+  switch( token_info->status ) {
+    case TokenStatusOK:
+    case TokenStatusEOF:
+      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';
+      }
+      MagickExceptionScript(OptionFatalError,"ScriptUnbalancedQuotes",
+           token_info->token,token_info->token_line,token_info->token_column);
+      break;
+    case TokenStatusMemoryFailed:
+      MagickExceptionScript(OptionFatalError,"ScriptTokenMemoryFailed","",
+           token_info->token_line,token_info->token_column);
+      break;
+    case TokenStatusBinary:
+      MagickExceptionScript(OptionFatalError,"ScriptIsBinary","",
+           token_info->curr_line,token_info->curr_column);
+      break;
+  }
 
-   /* 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
 /*
@@ -665,11 +389,7 @@ WandExport void ProcessCommandOptions(MagickCLI *cli_wand,int argc,
   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
+  for (i=0; i < end; i += count +1) {
       /* Finished processing one option? */
       if ( ( process_flags & ProcessOneOptionOnly ) != 0 && i != 0 )
         return;
@@ -690,44 +410,40 @@ WandExport void ProcessCommandOptions(MagickCLI *cli_wand,int argc,
       }
 
       if ( option_type == UndefinedOptionFlag ||
-           (option_type & NonMagickOptionFlag) != 0 )
-        {
+           (option_type & NonMagickOptionFlag) != 0 ) {
 #if MagickCommandDebug
-          (void) FormatLocaleFile(stderr, "CLI Non-Option: \"%s\"\n", option);
+        (void) FormatLocaleFile(stderr, "CLI Non-Option: \"%s\"\n", 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 ( ( 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;
+        if ( CLICatchException(cli_wand, MagickFalse) != MagickFalse )
+          break;
 
-          continue;
-        }
+        continue;
+      }
 
 
-      if ( (option_type & DeprecateOptionFlag) != 0 )
-        {
-          MagickExceptionContinue(OptionWarning,"DeprecatedOption",option,i);
-          if ( CLICatchException(cli_wand, MagickFalse) != MagickFalse )
-            break;
-          /* FALLTHRU - continue processing */
-        }
+      if ( (option_type & DeprecateOptionFlag) != 0 ) {
+        MagickExceptionContinue(OptionWarning,"DeprecatedOption",option,i);
+        if ( CLICatchException(cli_wand, MagickFalse) != MagickFalse )
+          break;
+        /* FALLTHRU - continue processing depreciated option */
+      }
 
-      if ((i+count) >= end )
-        {
-          MagickExceptionReturn(OptionError,"MissingArgument",option,i);
-          if ( CLICatchException(cli_wand, MagickFalse) != MagickFalse )
-            break;
-          continue; /* unable to proceed */
-        }
+      if ((i+count) >= end ) {
+        MagickExceptionReturn(OptionError,"MissingArgument",option,i);
+        if ( CLICatchException(cli_wand, MagickFalse) != MagickFalse )
+          break;
+        continue; /* no arguments unable to proceed */
+      }
 
       if (*option=='+') plus_alt_op = MagickTrue;
       if (*option!='+') arg1 = "true";
@@ -740,8 +456,7 @@ WandExport void ProcessCommandOptions(MagickCLI *cli_wand,int argc,
           option,(int) count,option_type,arg1,arg2);
 #endif
 
-      if ( (option_type & SpecialOptionFlag) != 0 )
-        {
+      if ( (option_type & SpecialOptionFlag) != 0 ) {
           if ( ( process_flags & ProcessExitOption ) != 0
                && LocaleCompare(option,"-exit") == 0 )
             return;
@@ -756,9 +471,7 @@ WandExport void ProcessCommandOptions(MagickCLI *cli_wand,int argc,
           CLISpecialOperator(cli_wand,option,arg1);
         }
 
-
-      if ( (option_type & SettingOptionFlags) != 0 )
-        {
+      if ( (option_type & SettingOptionFlags) != 0 ) {
           CLISettingOptionInfo(cli_wand, option+1, arg1);
           // FUTURE: Sync Specific Settings into Images
         }
@@ -771,7 +484,6 @@ WandExport void ProcessCommandOptions(MagickCLI *cli_wand,int argc,
 
       if ( CLICatchException(cli_wand, MagickFalse) != MagickFalse )
         break;
-
     }
 
   if ( CLICatchException(cli_wand, MagickFalse) != MagickFalse )
@@ -787,7 +499,7 @@ WandExport void ProcessCommandOptions(MagickCLI *cli_wand,int argc,
   option=argv[i];
 
 #if MagickCommandDebug
-  (void) FormatLocaleFile(stderr, "CLI Output: \"%s\"\n", option );
+  (void) FormatLocaleFile(stderr, "CLI Write File: \"%s\"\n", option );
 #endif
 
   // if stacks are not empty
@@ -803,8 +515,7 @@ WandExport void ProcessCommandOptions(MagickCLI *cli_wand,int argc,
     MagickExceptionReturn(OptionError,"MissingOutputFilename",option,i);
 
   /* If no images in MagickCLI */
-  if ( cli_wand->wand.images == (Image *) NULL )
-    {
+  if ( cli_wand->wand.images == (Image *) NULL ) {
       /* a "null:" output coder with no images is not an error! */
       if ( LocaleCompare(option,"null:") == 0 )
         return;
@@ -982,7 +693,13 @@ WandExport MagickBooleanType MagickImageCommand(ImageInfo *image_info,
   /* FUTURE: add this to 'operations.c' */
   cli_wand=AcquireMagickCLI(image_info,exception);
 
-  if (LocaleCompare("-script",argv[1]) == 0)
+  if (LocaleCompare("-list",argv[1]) == 0)
+    /* Special option, list information and exit
+       FUTURE: this should be a MagickCore option,
+       especially as no wand is actually needed!
+    */
+    CLISpecialOperator(cli_wand, argv[1]+1, argv[2]);
+  else if (LocaleCompare("-script",argv[1]) == 0)
     {
       /* Start processing directly from script, no pre-script options
         Replace wand command name with script name
@@ -990,20 +707,15 @@ WandExport MagickBooleanType MagickImageCommand(ImageInfo *image_info,
       GetPathComponent(argv[2],TailPath,cli_wand->wand.name);
       ProcessScriptOptions(cli_wand,argc-2,argv+2);
     }
-  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);
 
-  /* recover original image_info - check we get the right image_info */
+  /* recover original image_info from bottom of stack */
   while (cli_wand->image_info_stack != (Stack *)NULL)
     CLISpecialOperator(cli_wand,"}",(const char *)NULL);
 
+  /* assert we have recovered the original structures */
   assert(cli_wand->wand.image_info == image_info);
   assert(cli_wand->wand.exception == exception);
 
index 9cff0e395f7667a14dfc4083873a56dc9ed30cc2..c22369dbcdd374f91be5728eef70d0e6bf71737d 100644 (file)
@@ -52,6 +52,14 @@ struct _MagickCLI       /* CLI interface version of MagickWand */
     *image_list_stack,  /* Stacks of Image Lists and Image Info settings */
     *image_info_stack;
 
+  const char
+    *location,          /* Location string for exception reporting */
+    *filename;          /*  EG: "'%s' @ \"%s\" line %d column %d"
+                                    option, filename, line, column */
+  size_t
+    line,               /* location of current option for error above */
+    column;
+
   size_t
     signature;
 };
index e29c1b421aa55e31f97cc423cc0e731b40c9771a..43f1c5730a2bf9cdaa3bc32687d61d1ec56b96e4 100644 (file)
@@ -420,6 +420,10 @@ WandExport MagickCLI *AcquireMagickCLI(ImageInfo *image_info,
   cli_wand->quantize_info=AcquireQuantizeInfo(cli_wand->wand.image_info);
   cli_wand->image_list_stack=(Stack *)NULL;
   cli_wand->image_info_stack=(Stack *)NULL;
+  cli_wand->location="'%s' @ unset location";
+  cli_wand->filename="";
+  cli_wand->line=0;
+  cli_wand->column=0;
   cli_wand->signature=WandSignature;
 
   if (cli_wand->wand.debug != MagickFalse)
diff --git a/MagickWand/script-token-test-data.txt b/MagickWand/script-token-test-data.txt
new file mode 100644 (file)
index 0000000..4769b31
--- /dev/null
@@ -0,0 +1,42 @@
+#
+# Comments should be ignored
+#
+-option key  # end of line comment
++reset   imbedded#hash   # <- not a comment, thought this is
+
+This\ is' a 'single" token"
+
+And\\\ \''even '\'\""more \""complex
+
+"Backslash chars \n are returned as is"
+'regardless \n of quoting'
+
+'Single quote escapes'
+\' "'"
+
+"Double quote escapes"
+\" '"' "\""
+
+Back\ slash\ escapes
+\\ '\'  "\\"       # NOTE that backslash in single quotes are literial!
+
+'Space Character Escapes'
+\  ' '  " "
+
+'Empty Tokens, using quotes'
+''   ""
+
+"Unicode charcaters are handled"
+"° ' ²  ³  ` ´"
+"µ  ¶  ⨀  ⨁  ⨂"
+测试用的汉字
+
+Lines__\
+__Continuation
+
+'quoted_newlines__
+__are_part_of_token'
+
+"Last Token before EOF"
+
+
diff --git a/MagickWand/script-token-test.c b/MagickWand/script-token-test.c
new file mode 100644 (file)
index 0000000..f1222c2
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%                                                                             %
+%                                                                             %
+%    SSS    CCC  RRRR   III  PPPP  TTTTT    TTTTT  OOO   K  K  EEEE  N   N    %
+%   S      C     R   R   I   P   P   T        T   O   O  K K   E     NN  N    %
+%    SSS   C     RRRR    I   PPPP    T        T   O   O  KK    EEE   N N N    %
+%       S  C     R R     I   P       T        T   O   O  K K   E     N  NN    %
+%   SSSS    CCC  R  RR  III  P       T        T    OOO   K  K  EEEE  N   N    %
+%                                                                             %
+%                         TTTTT  EEEEE  SSSSS  TTTTT                          %
+%                           T    E      SS       T                            %
+%                           T    EEE     SSS     T                            %
+%                           T    E         SS    T                            %
+%                           T    EEEEE  SSSSS    T                            %
+%                                                                             %
+%       Perform "Magick" on Images via the Command Line Interface             %
+%                                                                             %
+%                             Dragon Computing                                %
+%                             Anthony Thyssen                                 %
+%                               January 2012                                  %
+%                                                                             %
+%                                                                             %
+%  Copyright 1999-2012 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  %
+%  obtain a copy of the License at                                            %
+%                                                                             %
+%    http://www.imagemagick.org/script/license.php                            %
+%                                                                             %
+%  Unless required by applicable law or agreed to in writing, software        %
+%  distributed under the License is distributed on an "AS IS" BASIS,          %
+%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
+%  See the License for the specific language governing permissions and        %
+%  limitations under the License.                                             %
+%                                                                             %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%  Test the raw tokanization of the  ScriptToken Subroutines
+%
+%  This actually uses very little of the magic core functions
+%  and in fact creates a completely stand-alone program by substituting
+%  required MagickCore with direct system equivelents.
+%
+%  Build
+%     cc     script-token-test.c   -o script-token-test
+%
+%  For testing see  script-token-test.sh
+%
+*/
+
+/* System Replacement for MagickWand includes */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <errno.h>
+
+#define MaxTextExtent     4096
+#define MagickFalse       0
+#define MagickTrue        1
+#define MagickBooleanType int
+
+#define AcquireMagickMemory(s)    malloc(s)
+#define RelinquishMagickMemory(p) (free(p),NULL)
+#define ResizeMagickMemory(p,s)   realloc(p,s)
+#define ResetMagickMemory(p,b,s)  memset(p,b,s)
+#define StringToLong(s)           strtol(s,(char **) NULL,10)
+#define LocaleCompare(p,q)        strcasecmp(p,q)
+#define LocaleNCompare(p,q,l)     strncasecmp(p,q,l)
+#define WandSignature             0xabacadabUL
+#define WandExport
+
+/* Include the actual code for ScriptToken functions */
+#define SCRIPT_TOKEN_TESTING  1 /* Prevent MagickWand Includes */
+#include "script-token.h"
+#include "script-token.c"
+
+/* Test program to report what tokens it finds in given input file/stream */
+
+int main(int argc, char *argv[])
+{
+  ScriptTokenInfo
+     *token_info;
+
+  token_info = AcquireScriptTokenInfo( (argc>1) ? argv[1] : "-" );
+  if (token_info == (ScriptTokenInfo *) NULL) {
+    printf("Script Open Failure : %s\n", strerror(errno));
+    return(1);
+  }
+
+  while (1) {
+    if( GetScriptToken(token_info) == MagickFalse )
+      break;
+
+    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';
+    }
+    printf("l=%d, c=%d, stat=%d, len=%d, token=\"%s\"\n",
+         token_info->token_line, token_info->token_column,
+         token_info->status, token_info->length, token_info->token);
+  }
+
+  switch( token_info->status ) {
+    case TokenStatusOK:
+      break;
+    case TokenStatusEOF:
+      printf("EOF Found\n");
+      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';
+      }
+      printf("Bad Quotes l=%d, c=%d  token=\"%s\"\n",
+           token_info->token_line,token_info->token_column, token_info->token);
+      break;
+    case TokenStatusMemoryFailed: /* token is invalid */
+      printf("Out of Memory  l=%d, c=%d\n",
+           token_info->token_line,token_info->token_column);
+      break;
+    case TokenStatusBinary:       /* token is invalid */
+      printf("Binary Char at l=%d, c=%d\n",
+           token_info->curr_line,token_info->curr_column);
+      break;
+  }
+
+  /* Clean up */
+  token_info = DestroyScriptTokenInfo(token_info);
+
+  return(0);
+}
diff --git a/MagickWand/script-token-test.sh b/MagickWand/script-token-test.sh
new file mode 100755 (executable)
index 0000000..b80f1ad
--- /dev/null
@@ -0,0 +1,34 @@
+#!/bin/sh
+#
+#
+# Basic testing of ScriptToken parser.
+#
+./script-token-test script-token-test-data.txt
+echo ""
+
+
+
+echo -n "\"Next token bad quotes\" \"unfinished quotes ->" |\
+   ./script-token-test
+echo ""
+
+
+
+perl -e 'print "\"Binary input follows\"\n", "abc\006xyz\n"' |\
+   ./script-token-test
+echo ""
+
+
+
+( echo '"Very BIG Token Tests"'
+  dd if=/dev/zero bs=80   count=1    2>/dev/null | tr '\0' 'a'; echo ""
+  dd if=/dev/zero bs=500  count=1    2>/dev/null | tr '\0' 'b'; echo ""
+  dd if=/dev/zero bs=4000 count=1    2>/dev/null | tr '\0' 'c'; echo ""
+  dd if=/dev/zero bs=5000 count=1    2>/dev/null | tr '\0' 'd'; echo ""
+  dd if=/dev/zero bs=10k  count=1    2>/dev/null | tr '\0' 'e'; echo ""
+  dd if=/dev/zero bs=13k  count=1    2>/dev/null | tr '\0' 'f'; echo ""
+  dd if=/dev/zero bs=8k   count=1024 2>/dev/null | tr '\0' 'e'; echo ""
+  echo '"and all is well!"'
+) | ./script-token-test
+echo ""
+
diff --git a/MagickWand/script-token.c b/MagickWand/script-token.c
new file mode 100644 (file)
index 0000000..9af2b37
--- /dev/null
@@ -0,0 +1,371 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%                                                                             %
+%                                                                             %
+%    SSS    CCC  RRRR   III  PPPP  TTTTT    TTTTT  OOO   K  K  EEEE  N   N    %
+%   S      C     R   R   I   P   P   T        T   O   O  K K   E     NN  N    %
+%    SSS   C     RRRR    I   PPPP    T        T   O   O  KK    EEE   N N N    %
+%       S  C     R R     I   P       T        T   O   O  K K   E     N  NN    %
+%   SSSS    CCC  R  RR  III  P       T        T    OOO   K  K  EEEE  N   N    %
+%                                                                             %
+%       Perform "Magick" on Images via the Command Line Interface             %
+%                                                                             %
+%                             Dragon Computing                                %
+%                             Anthony Thyssen                                 %
+%                               January 2012                                  %
+%                                                                             %
+%                                                                             %
+%  Copyright 1999-2012 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  %
+%  obtain a copy of the License at                                            %
+%                                                                             %
+%    http://www.imagemagick.org/script/license.php                            %
+%                                                                             %
+%  Unless required by applicable law or agreed to in writing, software        %
+%  distributed under the License is distributed on an "AS IS" BASIS,          %
+%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
+%  See the License for the specific language governing permissions and        %
+%  limitations under the License.                                             %
+%                                                                             %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%  Read a stream of characters and return tokens one at a time
+%
+*/
+\f
+/*
+  Include declarations.
+*/
+#ifndef SCRIPT_TOKEN_TESTING
+#include "MagickWand/studio.h"
+#include "MagickCore/memory_.h"
+#include "MagickCore/string-private.h"
+#include "MagickWand/script-token.h"
+#endif
+\f
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%                                                                             %
+%                                                                             %
+%                                                                             %
+%   A c q u i r e S c r i p t T o k e n I n f o                               %
+%                                                                             %
+%                                                                             %
+%                                                                             %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%  AcquireScriptTokenInfo() allocated, initializes and opens the given
+%  file stream from which tokens are to be extracted.
+%
+%  The format of the AcquireScriptTokenInfo method is:
+%
+%     ScriptTokenInfo *AcquireScriptTokenInfo(char *filename)
+%
+%  A description of each parameter follows:
+%
+%    o filename   the filename to open  ("-" means stdin)
+%
+*/
+WandExport ScriptTokenInfo *AcquireScriptTokenInfo(char *filename)
+{
+  ScriptTokenInfo
+    *token_info;
+
+  token_info=(ScriptTokenInfo *) AcquireMagickMemory(sizeof(*token_info));
+  if (token_info == (ScriptTokenInfo *) NULL)
+    return token_info;
+  (void) ResetMagickMemory(token_info,0,sizeof(*token_info));
+
+  token_info->opened=MagickFalse;
+  if ( LocaleCompare(filename,"-") == 0 ) {
+    token_info->stream=stdin;
+    token_info->opened=MagickFalse;
+  }
+#if 0  /* FUTURE POSIBILITIES */
+  else if ( LocaleNCompare(filename,"fd:",3) == 0 ) {
+    token_info->stream=fdopen(StringToLong(filename+3),"r");
+    token_info->opened=MagickFalse;
+  }
+#endif
+  else {
+    token_info->stream=fopen(filename, "r");
+    token_info->opened=MagickTrue;
+  }
+  if ( token_info->stream == (FILE *)NULL ) {
+    token_info=(ScriptTokenInfo *) RelinquishMagickMemory(token_info);
+    return(token_info);
+  }
+
+  token_info->curr_line=1;
+  token_info->length=INITAL_TOKEN_LENGTH;
+  token_info->token=(char *) AcquireMagickMemory(token_info->length);
+
+  token_info->status=(token_info->token != (char *)NULL)
+                      ? TokenStatusOK : TokenStatusMemoryFailed;
+  token_info->signature=WandSignature;
+
+  return token_info;
+}
+\f
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%                                                                             %
+%                                                                             %
+%                                                                             %
+%   D e s t r o y S c r i p t T o k e n I n f o                               %
+%                                                                             %
+%                                                                             %
+%                                                                             %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%  DestroyScriptTokenInfo() allocated, initializes and opens the given
+%  file stream from which tokens are to be extracted.
+%
+%  The format of the DestroyScriptTokenInfo method is:
+%
+%     ScriptTokenInfo *DestroyScriptTokenInfo(ScriptTokenInfo *token_info)
+%
+%  A description of each parameter follows:
+%
+%    o token_info   The ScriptTokenInfo structure to be destroyed
+%
+*/
+WandExport ScriptTokenInfo * DestroyScriptTokenInfo(ScriptTokenInfo *token_info)
+{
+  assert(token_info != (ScriptTokenInfo *) NULL);
+  assert(token_info->signature == WandSignature);
+
+  if ( token_info->opened != MagickFalse )
+    fclose(token_info->stream);
+
+  if (token_info->token != (char *) NULL )
+      token_info->token=RelinquishMagickMemory(token_info->token);
+  token_info=(ScriptTokenInfo *) RelinquishMagickMemory(token_info);
+  return(token_info);
+}
+\f
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%                                                                             %
+%                                                                             %
+%                                                                             %
+%   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 can be any length.
+%
+%  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 unquoted white-space or EOF condition has been found.  That is to say
+%  the file stream is parsed purely character-by-character, regardless any
+%  buffering constraints set by the system.  It is not parsed line-by-line.
+%
+%  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 but was incomplete due to some
+%  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 GetScriptToken method is:
+%
+%     MagickBooleanType GetScriptToken(ScriptTokenInfo *token_info)
+%
+%  A description of each parameter follows:
+%
+%    o token_info    pointer to a structure holding token details
+%
+*/
+/* States of the parser */
+#define IN_WHITE 0
+#define IN_TOKEN 1
+#define IN_QUOTE 2
+#define IN_COMMENT 3
+
+/* 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; \
+  if (c == EOF ) \
+    break; \
+  if ( (c>='\0' && c<'\a') || (c>'\r' && c<' ' && c!='\033') ) { \
+    token_info->status=TokenStatusBinary; \
+    break; \
+  } \
+}
+/* macro to collect the token characters */
+#define SaveChar(c) \
+{ \
+  if ((size_t) offset >= (token_info->length-1)) { \
+    if ( token_info->length >= MaxTextExtent ) \
+      token_info->length += MaxTextExtent; \
+    else \
+      token_info->length *= 4; \
+    token_info->token = (char *) \
+         ResizeMagickMemory(token_info->token, token_info->length); \
+    if ( token_info->token == (char *)NULL ) { \
+      token_info->status=TokenStatusMemoryFailed; \
+      break; \
+    } \
+  } \
+  token_info->token[offset++]=(char) (c); \
+}
+
+WandExport 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);
+
+    /* 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 == '\n' || c == '\r' )
+          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;
+    }
+  }
+  /* input stream has EOF or produced a fatal error */
+  token_info->token[offset]='\0';
+  if ( token_info->status != TokenStatusOK )
+    return(MagickFalse);  /* fatal condition - no valid token */
+  token_info->status = TokenStatusEOF;
+  if ( state == IN_QUOTE)
+    token_info->status = TokenStatusBadQuotes;
+  if ( state == IN_TOKEN)
+    return(MagickTrue);   /* token with EOF at end - no problem */
+  return(MagickFalse);    /* in white space or in quotes - invalid token */
+}
diff --git a/MagickWand/script-token.h b/MagickWand/script-token.h
new file mode 100644 (file)
index 0000000..d8dbbac
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+  Copyright 1999-2012 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.
+  obtain a copy of the License at
+  
+    http://www.imagemagick.org/script/license.php
+  
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+  MagickWand convert command-line method.
+*/
+#ifndef _SCRIPT_TOKEN_H
+#define _SCRIPT_TOKEN_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+/* Status of the Stream */
+typedef enum {
+  TokenStatusOK = 0,
+  TokenStatusEOF,
+  TokenStatusBadQuotes,
+  TokenStatusBinary,
+  TokenStatusMemoryFailed
+} TokenStatus;
+
+/* Initial length is MaxTextExtent/64 => 64  (divisor is a power of 4)
+   most tokens are never larger than this, so no need to waste memory!
+   Also no CLI option is larger than about 40 characters!
+*/
+#define INITAL_TOKEN_LENGTH  64
+typedef struct
+{
+  FILE
+    *stream;        /* the file stream we are reading from */
+
+  MagickBooleanType
+    opened;         /* was that stream opened? */
+
+  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,      /* start of last token (option or argument) */
+    token_column;
+
+  TokenStatus
+    status;         /* Have we reached EOF? see Token Status */
+
+  size_t
+    signature;
+} ScriptTokenInfo;
+
+
+extern WandExport ScriptTokenInfo
+  *AcquireScriptTokenInfo(char *),
+  *DestroyScriptTokenInfo(ScriptTokenInfo *);
+
+extern WandExport MagickBooleanType
+  GetScriptToken(ScriptTokenInfo *);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
index 3f07612f4011dcf280f67221b4b9591e2bbc514c..437b0f4b12fc6c850b8fd45680d695463d30f321 100644 (file)
         <message name="ScriptIsBinary">
           script is binary
         </message>
-        <message name="ScriptTokenTooBig">
+        <message name="ScriptTokenMemoryFailed">
           script token too big
         </message>
         <message name="ScriptUnbalancedQuotes">
index 0ecf5fa4ce04c818bed6466afa2dfa6978179b4d..4c4549b35dcee3f9fe0e98a433a750bbc1d72fe5 100644 (file)
         <message name="ScriptIsBinary">
           de script en binaire
         </message>
-        <message name="ScriptTokenTooBig">
+        <message name="ScriptTokenMemoryFailed">
           de script token trop gros
         </message>
         <message name="ScriptUnbalancedQuotes">