#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
/*
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 */
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);
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
/*
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;
}
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";
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;
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
}
if ( CLICatchException(cli_wand, MagickFalse) != MagickFalse )
break;
-
}
if ( CLICatchException(cli_wand, MagickFalse) != MagickFalse )
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
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;
/* 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
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);
--- /dev/null
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% 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 */
+}