% 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 "MagickCore/version.h"
-#include "MagickCore/string-private.h"
+#include "MagickWand/magick-cli.h"
+#include "MagickWand/script-token.h"
#include "MagickCore/utility-private.h"
+#include "MagickCore/exception-private.h"
+#include "MagickCore/version.h"
+\f
+/* verbose debugging,
+ 0 - no debug lines
+ 3 - show option details (better to use -debug Command now)
+ 5 - image counts (after option runs)
+*/
+#define MagickCommandDebug 0
+
\f
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
-+ M a g i c k C o m m a n d S p e c i a l %
++ P r o c e s s S c r i p t O p t i o n s %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
-% MagickS() Reads the various reads various options defining how
-% to read, process and write images. The options can be sourced from the
-% command line, or from script files, or pipelines.
+% 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 'index' argument of the argument array given.
%
-% Processing is performed using stack of expanded 'Wand' structures, which
-% not only define 'image_info' store of current global options, but also the
-% current input source of the options.
+% 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.
%
-% The format of the MagickImageCommand method is:
+% 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.
%
-% void MagickSpecialOption(MagickWand *wand,
-% const char *option, const char *arg)
+% The format of the ProcessScriptOptions method is:
+%
+% void ProcessScriptOptions(MagickCLI *cli_wand,const char *filename,
+% int argc,char **argv,int index)
%
% A description of each parameter follows:
%
-% o wand: the main CLI Wand to use.
+% o cli_wand: the main CLI Wand to use.
+%
+% o filename: the filename of script to process
%
-% o option: The special option (with any switch char) to process
+% o argc: the number of elements in the argument vector. (optional)
%
-% o arg: Argument for option, if required
+% o argv: A text array containing the command line arguments. (optional)
+%
+% o index: offset of next argment in argv (script arguments) (optional)
%
*/
-WandExport void MagickSpecialOption(MagickWand *wand,
- const char *option, const char *arg)
+WandExport void ProcessScriptOptions(MagickCLI *cli_wand,const char *filename,
+ int argc,char **argv,int index)
{
- if (LocaleCompare("-read",option) == 0)
- {
-#if 1
- /* MagickCore style of Read */
- Image *
- new_images;
-
- CopyMagickString(wand->image_info->filename,arg,MaxTextExtent);
- if (wand->image_info->ping != MagickFalse)
- new_images=PingImages(wand->image_info,wand->exception);
- else
- new_images=ReadImages(wand->image_info,wand->exception);
- AppendImageToList(&wand->images, new_images);
-#else
- /* MagickWand style of Read - append new images -- FAILS */
- MagickSetLastIterator(wand);
- MagickReadImage(wand, arg);
- MagickSetFirstIterator(wand);
-#endif
- return;
+ ScriptTokenInfo
+ *token_info;
+
+ CommandOptionFlags
+ option_type;
+
+ int
+ count;
+
+ char
+ *option,
+ *arg1,
+ *arg2;
+
+ assert(filename != (char *)NULL ); /* at least one argument - script name */
+ assert(cli_wand != (MagickCLI *) NULL);
+ assert(cli_wand->signature == WandSignature);
+ 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
+ cli_wand->filename=filename;
+
+ /* Process Options from Script */
+ option = arg1 = arg2 = (char*)NULL;
+ 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 */
}
+
+ do { /* use break to loop to exception handler and loop */
+
+ /* save option details */
+ CloneString(&option,token_info->token);
+
+ /* 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
- if (LocaleCompare(option,"(") == 0)
- // push images/settings
- if (LocaleCompare(option,")") == 0)
- // pop images/settings
- if (LocaleCompare(option,"respect_parenthesis") == 0)
- // adjust stack handling
- // Other 'special' options this should handle
- // "region" "clone" "list" "version" "noop" "sans*"?
- // It does not do "exit" however as due to its side-effect requirements
+ (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 - always for "magick-script" */
+ if ( option_type == UndefinedOptionFlag ||
+ (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 ( 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 */
+ }
+
+ if ( count >= 1 ) {
+ if( IfMagickFalse(GetScriptToken(token_info)) )
+ CLIWandException(OptionFatalError,"MissingArgument",option);
+ CloneString(&arg1,token_info->token);
+ }
+ else
+ 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);
+
+ /*
+ Process Options
+ */
+#if MagickCommandDebug >= 3
+ (void) FormatLocaleFile(stderr,
+ "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 */
+ }
+
+ /* MagickCommandGenesis() options have no place in a magick script */
+ if ( (option_type & GenesisOptionFlag) != 0 ) {
+ CLIWandException(OptionError,"InvalidUseOfOption",option);
+ break; /* next option */
+ }
+
+ /* 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 */
+ }
+
+ /* Process non-specific Option */
+ CLIOption(cli_wand, option, arg1, arg2);
+
+ } while (0); /* break block to next option */
+
+#if MagickCommandDebug >= 5
+ fprintf(stderr, "Script Image Count = %ld\n",
+ GetImageListLength(cli_wand->wand.images) );
+#endif
+ if ( IfMagickTrue(CLICatchException(cli_wand, MagickFalse)) )
+ break; /* exit loop */
+ }
+
+ /*
+ 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 */
+ token_info = DestroyScriptTokenInfo(token_info);
+
+ CloneString(&option,(char *)NULL);
+ CloneString(&arg1,(char *)NULL);
+ CloneString(&arg2,(char *)NULL);
+
+ return;
}
+\f
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
-+ M a g i c k C o m m a n d P r o c e s s O p t i o n s %
++ P r o c e s s C o m m a n d O p t i o n s %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
-% MagickCommandsProcessOptions() reads and processes arguments in the given
-% command line argument array.
+% ProcessCommandOptions() reads and processes arguments in the given
+% command line argument array. The 'index' defines where in the array we
+% should begin processing
%
-% The format of the MagickImageCommand method is:
+% 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
+% are to be handled, and if the last argument in array is to be regarded as a
+% final image write argument (filename or special coder).
+%
+% The format of the ProcessCommandOptions method is:
%
-% void MagickCommandArgs(MagickWand *wand,int argc,char **argv)
+% int ProcessCommandOptions(MagickCLI *cli_wand,
+% int argc,char **argv,int index)
%
% A description of each parameter follows:
%
-% o wand: the main CLI Wand to use.
+% o cli_wand: the main CLI Wand to use.
%
% o argc: the number of elements in the argument vector.
%
% o argv: A text array containing the command line arguments.
%
+% 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.
+%
*/
-#define MagickExceptionContinue(severity,tag,option,arg) \
- (void) ThrowMagickException(wand->exception,GetMagickModule(),severity,tag, \
- "'%s' arg#%lu", option, (unsigned long)arg)
-#define MagickExceptionReturn(severity,tag,option,arg) \
-{ \
- MagickExceptionContinue(severity,tag,option,arg); \
- return; \
-}
-
-WandExport void MagickCommandProcessOptions(MagickWand *wand,int argc,
- char **argv)
+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;
CommandOptionFlags
- flags;
+ option_type;
- assert(wand != (MagickWand *) NULL);
- assert(wand->signature == WandSignature);
- assert(wand->draw_info != (DrawInfo *) NULL); /* ensure it is a CLI wand */
- if (wand->debug != MagickFalse)
- (void) LogMagickEvent(WandEvent,GetMagickModule(),"%s",wand->name);
+ 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);
- /*
- Parse command-line options.
- */
- count=0;
- for (i=1; i < (ssize_t) (argc-1); i+=count+1)
- {
- option=argv[i];
- plus_alt_op = MagickFalse;
- arg1=(char *)NULL;
- arg2=(char *)NULL;
+ /* 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 */
- /* FUTURE: merge these into one call */
- count=ParseCommandOption(MagickCommandOptions,MagickFalse,argv[i]);
- flags=(CommandOptionFlags) GetCommandOptionFlags(
- MagickCommandOptions,MagickFalse,argv[i]);
+ if (IfMagickTrue(cli_wand->wand.debug))
+ (void) CLILogEvent(cli_wand,CommandEvent,GetMagickModule(),
+ "- Starting (\"%s\")", argv[index]);
-#define MagickCommandDebug 0
+ end = argc;
+ if ( (cli_wand->process_flags & ProcessImplictWrite) != 0 )
+ end--; /* the last arument is an implied write, do not process directly */
- if ( count == -1 || flags == UndefinedOptionFlag ||
- (flags & NonConvertOptionFlag) != 0 )
- {
- count = 0;
-#if MagickCommandDebug
- (void) FormatLocaleFile(stderr, "CLI Non-Option: \"%s\"\n", option);
-#endif
- if (IsCommandOption(option) == MagickFalse)
- {
- /* non-option -- treat as a image read */
- MagickSpecialOption(wand,"-read",option);
- continue;
- }
- else
- MagickExceptionReturn(OptionError,"UnrecognizedOption",option,i);
- }
-
- if ( (flags & DeprecateOptionFlag) != 0 )
- MagickExceptionContinue(OptionWarning,"DeprecatedOption",option,i);
- /* continue processing option anyway */
+ for (i=index; i < end; i += count +1) {
+ /* Finished processing one option? */
+ if ( (cli_wand->process_flags & ProcessOneOptionOnly) != 0 && i != index )
+ return(i);
- if ((i+count) > (ssize_t) (argc-1))
- MagickExceptionReturn(OptionError,"MissingArgument",option,i);
- if (*option=='+') plus_alt_op = MagickTrue;
- if (*option!='+') arg1 = "true";
- if ( count >= 1 ) arg1 = argv[i+1];
- if ( count >= 2 ) arg2 = argv[i+2];
+ do { /* use break to loop to exception handler and loop */
-#if MagickCommandDebug
- (void) FormatLocaleFile(stderr,
- "CLI Option: \"%s\" \tCount: %d Flags: %04x Args: \"%s\" \"%s\"\n",
- option,count,flags,arg1,arg2);
-#endif
+ option=argv[i];
+ cli_wand->line=i; /* note the argument for this option */
- if ( (flags & SpecialOptionFlag) != 0 )
- {
- if (LocaleCompare(option,"-exit") == 0)
- return;
+ /* 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
- if (LocaleCompare(option,"-script") == 0)
- {
- // Unbalanced Parenthesis if stack not empty
- // Call Script, with filename as argv[0]
- return;
- }
+ (void) FormatLocaleFile(stderr, "CLI %d: \"%s\" matched \"%s\"\n",
+ i, argv[i], cli_wand->command->mnemonic );
#endif
- MagickSpecialOption(wand,option,arg1);
- }
- if ( (flags & SettingOptionFlags) != 0 )
- {
- WandSettingOptionInfo(wand, option+1, arg1);
- // FUTURE: Sync Specific Settings into Images
+ if ( option_type == UndefinedOptionFlag ||
+ (option_type & NonMagickOptionFlag) != 0 ) {
+#if MagickCommandDebug >= 3
+ (void) FormatLocaleFile(stderr, "CLI arg %d Non-Option: \"%s\"\n",
+ i, option);
+#endif
+ 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 ((i+count) >= end ) {
+ CLIWandException(OptionFatalError,"MissingArgument",option);
+ if ( CLICatchException(cli_wand, MagickFalse) != MagickFalse )
+ return(end);
+ break; /* next option - not that their is any! */
+ }
+
+ arg1 = ( count >= 1 ) ? argv[i+1] : (char *)NULL;
+ arg2 = ( count >= 2 ) ? argv[i+2] : (char *)NULL;
+
+ /*
+ Process Known Options
+ */
+#if MagickCommandDebug >= 3
+ (void) FormatLocaleFile(stderr,
+ "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 */
+ }
+
+ /* Process standard image option */
+ CLIOption(cli_wand, option, arg1, arg2);
+
+ } 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 ( (flags & SimpleOperatorOptionFlag) != 0)
- {
- WandSimpleOperatorImages(wand, plus_alt_op, option+1, arg1, arg2);
- }
+ if ( (cli_wand->process_flags & ProcessImplictWrite) == 0 )
+ return(end); /* no implied write -- just return to caller */
- if ( (flags & ListOperatorOptionFlag) != 0 )
- {
- WandListOperatorImages(wand, plus_alt_op, option+1, arg1, arg2);
- }
+ assert(end==argc-1); /* end should not include last argument */
- // FUTURE: '-regard_warning' causes IM to exit more prematurely!
- // Note pipelined options may like more control over this level
- if (wand->exception->severity > ErrorException)
- {
- if (wand->exception->severity > ErrorException)
- //(regard_warnings != MagickFalse))
- return; /* FATAL - let caller handle */
- CatchException(wand->exception); /* output warnings and clear!!! */
- }
- }
-
- /* FUTURE: in the following produce a better error report
- -- Missing Output filename
+ /*
+ Implicit Write of images to final CLI argument
*/
-
- assert(i!=(ssize_t)(argc-1));
- option=argv[i]; /* the last argument - output filename! */
-
-#if MagickCommandDebug
- (void) FormatLocaleFile(stderr, "CLI Output: \"%s\"\n", option );
+ option=argv[i];
+ cli_wand->line=i;
+
+ /* 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 MagickCommandDebug >= 3
+ (void) FormatLocaleFile(stderr,"CLI arg %d Write File: \"%s\"\n",i,option);
#endif
- // if stacks are not empty
- // ThrowConvertException(OptionError,"UnbalancedParenthesis",option,i);
-
-
- /* This is the only value '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)
- MagickExceptionReturn(OptionError,"MissingAnImageFilename",option,i);
-
- /* If no images */
- if ( wand->images == (Image *) NULL )
- {
- /* a "null:" output coder with no images is ok */
- if ( LocaleCompare(option,"null:") == 0 )
- return;
- MagickExceptionReturn(OptionError,"MissingAnImageFilename",option,i);
- }
-
- /*
- Write out final image!
- */
- //WandListOperatorImages(wand,MagickFalse,"write",option,(const char *)NULL);
- (void) SyncImagesSettings(wand->image_info,wand->images,wand->exception);
- (void) WriteImages(wand->image_info,wand->images,option,wand->exception);
+ 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);
+ }
- return;
+ cli_wand->command=(const OptionInfo *)NULL;
+ CLIOption(cli_wand,"-write",option);
+ return(argc);
}
\f
/*
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
-% MagickImageCommand() Handle special 'once only' CLI arguments and
-% prepare to process the command line using a special CLI Magick Wand
-% via the MagickImageProcess() function.
+% MagickImageCommand() Handle special use CLI arguments and prepare a
+% CLI MagickCLI to process the command line or directly specified script.
+%
+% This is essentualy interface function between the MagickCore library
+% initialization function MagickCommandGenesis(), and the option MagickCLI
+% processing functions ProcessCommandOptions() or ProcessScriptOptions()
%
% 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;
}
/*
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,
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);
WandExport MagickBooleanType MagickImageCommand(ImageInfo *image_info,
int argc,char **argv,char **metadata,ExceptionInfo *exception)
{
- MagickWand
- *wand;
+ 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);
- }
- }
- if (argc < 3)
- return(MagickUsage());
+ size_t
+ len;
+
+ assert(image_info != (ImageInfo *)NULL);
+
+ /* For specific OS command line requirements */
ReadCommandlLine(argc,&argv);
-#if 0
- status=ExpandFilenames(&argc,&argv);
- if (status == MagickFalse)
- ThrowConvertException(ResourceLimitError,"MemoryAllocationFailed",
- GetExceptionMessage(errno));
-#endif
- if (LocaleCompare("-concatenate",argv[1]) == 0)
- return(ConcatenateImages(argc,argv,exception));
-
- /* create a special CLI Wand to hold all working settings */
- /* FUTURE: add this to 'operations.c' */
- wand=NewMagickWand();
- wand->image_info=DestroyImageInfo(wand->image_info);
- wand->image_info=image_info;
- wand->exception=DestroyExceptionInfo(wand->exception);
- wand->exception=exception;
- wand->draw_info=CloneDrawInfo(image_info,(DrawInfo *) NULL);
- wand->quantize_info=AcquireQuantizeInfo(image_info);
-
- if (LocaleCompare("-list",argv[1]) == 0)
- WandSettingOptionInfo(wand, argv[1]+1, argv[2]);
- else
- MagickCommandProcessOptions(wand,argc,argv);
- assert(wand->exception == exception);
- assert(wand->image_info == image_info);
+ /* Initialize special "CLI Wand" to hold images and settings (empty) */
+ cli_wand=AcquireMagickCLI(image_info,exception);
+ cli_wand->location="Initializing";
+ cli_wand->filename=argv[0];
+ cli_wand->line=1;
- /* Handle metadata for ImageMagickObject COM object for Windows VBS */
- if (metadata != (char **) NULL)
- {
- const char
- *format;
+ if (IfMagickTrue(cli_wand->wand.debug))
+ (void) CLILogEvent(cli_wand,CommandEvent,GetMagickModule(),
+ "\"%s\"",argv[0]);
- char
- *text;
- format="%w,%h,%m"; // Get this from image_info Option splaytree
+ GetPathComponent(argv[0],TailPath,cli_wand->wand.name);
+ SetClientName(cli_wand->wand.name);
+ ConcatenateMagickString(cli_wand->wand.name,"-CLI",MaxTextExtent);
- text=InterpretImageProperties(image_info,wand->images,format,exception);
- if (text == (char *) NULL)
- ThrowMagickException(exception,GetMagickModule(),ResourceLimitError,
- "MemoryAllocationFailed","`%s'", GetExceptionMessage(errno));
- else
- {
- (void) ConcatenateString(&(*metadata),text);
- text=DestroyString(text);
- }
+ 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;
+ }
+ }
+
+ /* 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 */
+
+ 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)
+ 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;
+
+ 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);
+ }
+ }
+
+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]);
/* Destroy the special CLI Wand */
- wand->exception = (ExceptionInfo *)NULL;
- wand->image_info = (ImageInfo *)NULL;
- wand=DestroyMagickWand(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));
}