2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6 % M M AAA GGGG IIIII CCCC K K %
7 % MM MM A A G I C K K %
8 % M M M AAAAA G GGG I C KKK %
9 % M M A A G G I C K K %
10 % M M A A GGGG IIIII CCCC K K %
18 % Perform "Magick" on Images via the Command Line Interface %
25 % Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization %
26 % dedicated to making software imaging solutions freely available. %
28 % You may not use this file except in compliance with the License. You may %
29 % obtain a copy of the License at %
31 % http://www.imagemagick.org/script/license.php %
33 % Unless required by applicable law or agreed to in writing, software %
34 % distributed under the License is distributed on an "AS IS" BASIS, %
35 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
36 % See the License for the specific language governing permissions and %
37 % limitations under the License. %
39 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
41 % Read CLI arguments, script files, and pipelines, to provide options that
42 % manipulate images from many different formats.
49 #include "MagickWand/studio.h"
50 #include "MagickWand/MagickWand.h"
51 #include "MagickWand/magick-wand-private.h"
52 #include "MagickCore/memory_.h"
53 #include "MagickCore/string-private.h"
54 #include "MagickWand/operation.h"
55 #include "MagickCore/utility-private.h"
56 #include "MagickCore/version.h"
58 #define MagickCommandDebug 0
60 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
64 % G e t S c r i p t T o k e n %
68 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
70 % GetScriptToken() is fairly general, finite state token parser. That will
71 % divide a input file stream into tokens, in a way that is almost identical
74 % It returns 'MagickTrue' if a token was found. Even in the special case of
75 % a empty token followed immediatally by a EOF. For example: ''{EOF}
77 % A token is returned immediatally the end of token is found. That is
78 % parsing is purely character by character, and not line-by-line. This
79 % should allow for mixed stream of tokens (options), and other data (images)
80 % without problems. Assuming the other data has a well defined End-Of-Data
81 % handling (see complex example below).
83 % Tokens are white space separated, and may be quoted, or even partially
84 % quoted by either single or double quotes, or the use of backslashes,
85 % or any mix of the three.
87 % For example: This\ is' a 'single" token"
89 % Single quotes will preserve all characters including backslashes. Double
90 % quotes will also preserve backslashes unless escaping a double quote,
91 % or another backslashes. Other shell meta-characters are not treated as
92 % special by this tokenizer.
94 % For example Quoting the quote chars:
95 % \' "'" \" '"' "\"" \\ '\' "\\"
97 % Comments start with a '#' character at the start of a new token (generally
98 % at start of a line, or after a unquoted white space charcater) and continue
99 % to the end of line. The are simply ignored. You can escape a comment '#'
100 % character to return a token that starts with such a character.
102 % More complex example...
103 % Sending a PGM image in the middle of a standard input script.
105 % magick -script - <<END
106 % # read a stdin in-line image...
107 % "pgm:-[0]" P2 2 2 3 0 1 1 2
108 % # continue processing that image
110 % -write enlarged.png
113 % Only a single space character separates the 'image read' from the
114 % 'image data' after which the next operation is read. This only works
115 % for image data formats with a well defined length or End-of-Data marker
116 % such as MIFF, and PbmPlus file formats.
118 % The format of the MagickImageCommand method is:
120 % MagickBooleanType GetScriptToken(ScriptTokenInfo *token_info)
122 % A description of each parameter follows:
124 % o token_info pointer to a structure holding token details
128 /* States of the parser */
137 TokenStatusBadQuotes,
138 TokenStatusTokenTooBig,
145 *stream; /* the file stream we are reading from */
148 *token; /* array of characters to holding details of he token */
151 length, /* length of token char array */
152 curr_line, /* current location in script file */
154 token_line, /* location of the start of this token */
158 status; /* Have we reached EOF? see Token Status */
161 /* macro to read character from stream */
164 c=fgetc(token_info->stream); \
165 token_info->curr_column++; \
167 token_info->curr_line++, token_info->curr_column=0; \
169 /* macro to collect the token characters */
170 #define SaveChar(c) \
172 if ((size_t) offset >= (token_info->length-1)) \
173 { token_info->token[offset++]='\0'; \
174 token_info->status=TokenStatusTokenTooBig; \
175 return(MagickFalse); \
177 token_info->token[offset++]=(char) (c); \
180 static MagickBooleanType GetScriptToken(ScriptTokenInfo *token_info)
193 /* EOF - no more tokens! */
194 if (token_info->status != TokenStatusOK)
196 token_info->token[0]='\0';
207 if (c == '\0' || c == EOF)
210 /* hash comment handling */
211 if ( state == IN_COMMENT )
216 if (c == '#' && state == IN_WHITE)
218 /* whitespace break character */
219 if (strchr(" \n\r\t",c) != (char *)NULL)
224 token_info->token[offset]='\0';
232 /* quote character */
233 if (strchr("'\"",c) != (char *)NULL)
238 token_info->token_line=token_info->curr_line;
239 token_info->token_column=token_info->curr_column;
256 /* escape char (preserve in quotes - unless escaping the same quote) */
259 if ( state==IN_QUOTE && quote == '\'' )
265 if (c == '\0' || c == EOF)
273 token_info->token_line=token_info->curr_line;
274 token_info->token_column=token_info->curr_column;
278 if (c != quote && c != '\\')
285 /* ordinary character */
289 token_info->token_line=token_info->curr_line;
290 token_info->token_column=token_info->curr_column;
300 /* stream has EOF or produced a fatal error */
301 token_info->token[offset]='\0';
302 token_info->status = TokenStatusEOF;
303 if (state == IN_QUOTE)
304 token_info->status = TokenStatusBadQuotes;
306 token_info->status = TokenStatusBinary;
307 if (state == IN_TOKEN && token_info->status == TokenStatusEOF)
313 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
317 + P r o c e s s S p e c i a l O p t i o n %
321 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
323 % ProcessSpecialOption() Apply certian options that are specific to Shell
324 % API interface. Specifically reading images and handling image and
325 % image_info (settings) stacks.
327 % The format of the ProcessSpecialOption method is:
329 % void ProcessSpecialOption(MagickWand *wand,const char *option,
330 % const char *arg, ProcessOptionFlags process_flags )
332 % A description of each parameter follows:
334 % o wand: the main CLI Wand to use.
336 % o option: The special option (with any switch char) to process
338 % o arg: Argument for option, if required
340 % o process_flags: Wether to process specific options or not.
343 WandExport void ProcessSpecialOption(MagickWand *wand,
344 const char *option, const char *arg, ProcessOptionFlags process_flags)
346 if ( LocaleCompare("-read",option) == 0 )
351 CopyMagickString(wand->image_info->filename,arg,MaxTextExtent);
352 if (wand->image_info->ping != MagickFalse)
353 new_images=PingImages(wand->image_info,wand->exception);
355 new_images=ReadImages(wand->image_info,wand->exception);
356 AppendImageToList(&wand->images, new_images);
359 if (LocaleCompare("-sans",option) == 0)
361 if (LocaleCompare("-sans0",option) == 0)
363 if (LocaleCompare("-sans2",option) == 0)
365 if (LocaleCompare("-noop",option) == 0)
369 if (LocaleCompare(option,"(") == 0)
370 // push images/settings
371 if (LocaleCompare(option,")") == 0)
372 // pop images/settings
373 if (LocaleCompare(option,"respect_parenthesis") == 0)
374 // adjust stack handling
375 // Other 'special' options this should handle
376 // "region" "clone" "list" "version"
377 // It does not do "exit" however as due to its side-effect requirements
379 if ( ( process_flags & ProcessUnknownOptionError ) != 0 )
380 MagickExceptionReturn(OptionError,"InvalidUseOfOption",option);
385 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
389 + P r o c e s s S c r i p t O p t i o n s %
393 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
395 % ProcessScriptOptions() reads options and processes options as they are
396 % found in the given file, or pipeline. The filename to open and read
397 % options is given as the zeroth argument of the argument array given.
399 % A script not 'return' to the command line processing, nor can they
400 % call (and return from) other scripts. At least not at this time.
402 % However special script options may used to read and process the other
403 % argument provided, typically those that followed a "-script" option on the
404 % command line. These extra script arguments may be interpreted as being
405 % images to read or write, settings (strings), or more options to be
406 % processed. How they are treated is up to the script being processed.
408 % The format of the ProcessScriptOptions method is:
410 % void ProcessScriptOptions(MagickWand *wand,int argc,char **argv)
412 % A description of each parameter follows:
414 % o wand: the main CLI Wand to use.
416 % o argc: the number of elements in the argument vector.
418 % o argv: A text array containing the command line arguments.
421 #define MagickExceptionScript(severity,tag,arg,line,col) \
422 (void) ThrowMagickException(wand->exception,GetMagickModule(),severity,tag, \
423 "'%s' : Line %u Column %u of script \"%s\"", arg, line, col, wand->name);
425 WandExport void ProcessScriptOptions(MagickWand *wand,int argc,
437 option_line, /* line and column of current option */
450 assert(argc>0 && argv[argc-1] != (char *)NULL);
451 assert(wand != (MagickWand *) NULL);
452 assert(wand->signature == WandSignature);
453 assert(wand->draw_info != (DrawInfo *) NULL); /* ensure it is a CLI wand */
454 if (wand->debug != MagickFalse)
455 (void) LogMagickEvent(WandEvent,GetMagickModule(),"%s",wand->name);
457 /* Initialize variables */
458 /* FUTURE handle file opening for '-' 'fd:N' or script filename */
459 file_opened=MagickFalse;
460 if ( LocaleCompare(argv[0],"-") == 0 )
462 CopyMagickString(wand->name,"stdin",MaxTextExtent);
463 token_info.stream=stdin;
464 file_opened=MagickFalse;
468 GetPathComponent(argv[0],TailPath,wand->name);
469 token_info.stream=fopen(argv[0], "r");
470 file_opened=MagickTrue;
473 option = arg1 = arg2 = (char*)NULL;
474 token_info.curr_line=1;
475 token_info.curr_column=0;
476 token_info.status=TokenStatusOK;
477 token_info.length=MaxTextExtent;
478 token_info.token=(char *) AcquireQuantumMemory(MaxTextExtent,sizeof(char));
479 if (token_info.token == (char *) NULL)
481 if ( file_opened != MagickFalse )
482 fclose(token_info.stream);
483 MagickExceptionScript(ResourceLimitError,"MemoryAllocationFailed","",0,0);
484 (void) ThrowMagickException(wand->exception,GetMagickModule(),
485 ResourceLimitError,"MemoryAllocationFailed","script token buffer");
489 /* Process Options from Script */
493 if( GetScriptToken(&token_info) == MagickFalse )
496 /* option length sanity check */
497 if( strlen(token_info.token) > 40 )
498 { token_info.token[37] = '.';
499 token_info.token[38] = '.';
500 token_info.token[39] = '.';
501 token_info.token[40] = '\0';
502 MagickExceptionScript(OptionFatalError,"UnrecognizedOption",
503 token_info.token,token_info.token_line,token_info.token_column);
507 /* save option details */
508 CloneString(&option,token_info.token);
509 option_line=token_info.token_line;
510 option_column=token_info.token_column;
512 #if MagickCommandDebug
513 (void) FormatLocaleFile(stderr, "Script Option Token: %u,%u: \"%s\"\n",
514 option_line, option_column, option );
516 /* get option type and argument count */
517 { const OptionInfo *option_info = GetCommandOptionInfo(option);
518 count=option_info->type;
519 option_type=option_info->flags;
520 #if MagickCommandDebug >= 2
521 (void) FormatLocaleFile(stderr, "option \"%s\" matched \"%s\"\n",
522 option, option_info->mnemonic );
526 /* handle a undefined option - image read? */
527 if ( option_type == UndefinedOptionFlag ||
528 (option_type & NonMagickOptionFlag) != 0 )
530 #if MagickCommandDebug
531 (void) FormatLocaleFile(stderr, "Script Non-Option: \"%s\"\n", option);
533 if ( IsCommandOption(option) == MagickFalse)
535 /* non-option -- treat as a image read */
536 ProcessSpecialOption(wand,"-read",option,MagickScriptReadFlags);
540 MagickExceptionScript(OptionFatalError,"UnrecognizedOption",
541 option,option_line,option_column);
545 plus_alt_op = MagickFalse;
546 if (*option=='+') plus_alt_op = MagickTrue;
550 if( GetScriptToken(&token_info) == MagickFalse )
552 MagickExceptionScript(OptionError,"MissingArgument",option,
553 option_line,option_column);
556 CloneString(&arg1,token_info.token);
559 CloneString(&arg1,(*option!='+')?"true":(char *)NULL);
563 if( GetScriptToken(&token_info) == MagickFalse )
565 MagickExceptionScript(OptionError,"MissingArgument",option,
566 option_line,option_column);
569 CloneString(&arg2,token_info.token);
572 CloneString(&arg2,(char *)NULL);
574 /* handle script special options */
575 //either continue processing command line
576 // or making use of the command line options.
577 //ProcessCommandOptions(wand,count+1,argv, MagickScriptArgsFlags);
579 #if MagickCommandDebug
580 (void) FormatLocaleFile(stderr,
581 "Script Option: \"%s\" \tCount: %d Flags: %04x Args: \"%s\" \"%s\"\n",
582 option,(int) count,option_type,arg1,arg2);
585 /* Process non-script specific option from file */
586 if ( (option_type & SpecialOptionFlag) != 0 )
588 if ( LocaleCompare(option,"-exit") == 0 )
590 /* No "-script" from script at this time */
591 ProcessSpecialOption(wand,option,arg1,MagickScriptReadFlags);
594 if ( (option_type & SettingOptionFlags) != 0 )
596 WandSettingOptionInfo(wand, option+1, arg1);
597 // FUTURE: Sync Specific Settings into Images
600 if ( (option_type & SimpleOperatorOptionFlag) != 0)
601 WandSimpleOperatorImages(wand, plus_alt_op, option+1, arg1, arg2);
603 if ( (option_type & ListOperatorOptionFlag) != 0 )
604 WandListOperatorImages(wand, plus_alt_op, option+1, arg1, arg2);
606 // FUTURE: '-regard_warning' causes IM to exit more prematurely!
607 // Note pipelined options may like more control over this level
608 if (wand->exception->severity > ErrorException)
610 if (wand->exception->severity > ErrorException)
611 //(regard_warnings != MagickFalse))
612 break; /* FATAL - caller handles exception */
613 CatchException(wand->exception); /* output warnings and clear!!! */
616 #if MagickCommandDebug
617 (void) FormatLocaleFile(stderr, "Script End: %d\n", token_info.status);
619 /* token sanity for error report */
620 if( strlen(token_info.token) > 40 )
621 { token_info.token[37] = '.';
622 token_info.token[38] = '.';
623 token_info.token[39] = '.';
624 token_info.token[40] = '\0';
627 switch( token_info.status )
629 case TokenStatusBadQuotes:
630 MagickExceptionScript(OptionFatalError,"ScriptUnbalancedQuotes",
631 token_info.token,token_info.token_line,token_info.token_column);
633 case TokenStatusTokenTooBig:
634 MagickExceptionScript(OptionFatalError,"ScriptTokenTooBig",
635 token_info.token,token_info.token_line,token_info.token_column);
637 case TokenStatusBinary:
638 MagickExceptionScript(OptionFatalError,"ScriptIsBinary","",
639 token_info.curr_line,token_info.curr_column);
647 if ( file_opened != MagickFalse )
648 fclose(token_info.stream);
650 CloneString(&option,(char *)NULL);
651 CloneString(&arg1,(char *)NULL);
652 CloneString(&arg2,(char *)NULL);
658 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
662 + P r o c e s s C o m m a n d O p t i o n s %
666 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
668 % ProcessCommandOptions() reads and processes arguments in the given
669 % command line argument array. The array does not contain the command
670 % being processed, only the options.
672 % The 'process_flags' can be used to control and limit option processing.
673 % For example, to only process one option, or how unknown and special options
674 % are to be handled, and if the last argument in array is to be regarded as a
675 % final image write argument (filename or special coder).
677 % The format of the ProcessCommandOptions method is:
679 % int ProcessCommandOptions(MagickWand *wand,int argc,char **argv,
680 % int *index, ProcessOptionFlags process_flags )
682 % A description of each parameter follows:
684 % o wand: the main CLI Wand to use.
686 % o argc: the number of elements in the argument vector.
688 % o argv: A text array containing the command line arguments.
690 % o process_flags: What type of arguments we are allowed to process
693 /* FUTURE: correctly identify option... CLI arg, Script line,column */
694 #define MagickExceptionContinue(severity,tag,arg,index) \
695 (void) ThrowMagickException(wand->exception,GetMagickModule(),severity,tag, \
696 "'%s' : CLI Arg #%d", arg, (int) index); \
698 #define MagickExceptionReturn(severity,tag,option,arg) \
700 MagickExceptionContinue(severity,tag,option,arg); \
704 WandExport void ProcessCommandOptions(MagickWand *wand,int argc,
705 char **argv, ProcessOptionFlags process_flags )
723 assert(argc>0 && argv[argc-1] != (char *)NULL);
724 assert(wand != (MagickWand *) NULL);
725 assert(wand->signature == WandSignature);
726 assert(wand->draw_info != (DrawInfo *) NULL); /* ensure it is a CLI wand */
727 if (wand->debug != MagickFalse)
728 (void) LogMagickEvent(WandEvent,GetMagickModule(),"%s",wand->name);
731 Parse command-line options.
734 if ( ( process_flags & ProcessOutputFile ) != 0 )
736 for (i=0; i < end; i += count +1)
738 #if MagickCommandDebug >= 2
739 (void) FormatLocaleFile(stderr, "index= %d\n", i );
741 /* Finished processing one option? */
742 if ( ( process_flags & ProcessOneOptionOnly ) != 0 && i != 0 )
746 plus_alt_op = MagickFalse;
751 { const OptionInfo *option_info = GetCommandOptionInfo(argv[i]);
752 count=option_info->type;
753 option_type=option_info->flags;
754 #if MagickCommandDebug >= 2
755 (void) FormatLocaleFile(stderr, "option \"%s\" matched \"%s\"\n",
756 argv[i], option_info->mnemonic );
760 if ( option_type == UndefinedOptionFlag ||
761 (option_type & NonMagickOptionFlag) != 0 )
763 #if MagickCommandDebug
764 (void) FormatLocaleFile(stderr, "CLI Non-Option: \"%s\"\n", option);
766 if ( IsCommandOption(option) != MagickFalse )
768 if ( ( process_flags & ProcessNonOptionImageRead ) != 0 )
770 /* non-option -- treat as a image read */
771 ProcessSpecialOption(wand,"-read",option,process_flags);
775 else if ( ( process_flags & ProcessUnknownOptionError ) != 0 )
777 MagickExceptionReturn(OptionFatalError,"UnrecognizedOption",
784 if ( (option_type & DeprecateOptionFlag) != 0 )
785 MagickExceptionContinue(OptionWarning,"DeprecatedOption",option,i);
786 /* continue processing option anyway */
788 if ((i+count) >= end )
789 MagickExceptionReturn(OptionError,"MissingArgument",option,i);
791 if (*option=='+') plus_alt_op = MagickTrue;
792 if (*option!='+') arg1 = "true";
793 if ( count >= 1 ) arg1 = argv[i+1];
794 if ( count >= 2 ) arg2 = argv[i+2];
796 #if MagickCommandDebug
797 (void) FormatLocaleFile(stderr,
798 "CLI Option: \"%s\" \tCount: %d Flags: %04x Args: \"%s\" \"%s\"\n",
799 option,(int) count,option_type,arg1,arg2);
802 if ( (option_type & SpecialOptionFlag) != 0 )
804 if ( ( process_flags & ProcessExitOption ) != 0
805 && LocaleCompare(option,"-exit") == 0 )
807 if ( ( process_flags & ProcessScriptOption ) != 0
808 && LocaleCompare(option,"-script") == 0)
810 // Unbalanced Parenthesis if stack not empty
811 // Call Script, with a filename as a zeroth argument
812 ProcessScriptOptions(wand,argc-(i+1),argv+(i+1));
815 ProcessSpecialOption(wand,option,arg1,process_flags);
818 if ( (option_type & SettingOptionFlags) != 0 )
820 WandSettingOptionInfo(wand, option+1, arg1);
821 // FUTURE: Sync Specific Settings into Images
824 if ( (option_type & SimpleOperatorOptionFlag) != 0)
825 WandSimpleOperatorImages(wand, plus_alt_op, option+1, arg1, arg2);
827 if ( (option_type & ListOperatorOptionFlag) != 0 )
828 WandListOperatorImages(wand, plus_alt_op, option+1, arg1, arg2);
830 // FUTURE: '-regard_warning' causes IM to exit more prematurely!
831 // Note pipelined options may like more control over this level
832 if (wand->exception->severity > ErrorException)
834 if (wand->exception->severity > ErrorException)
835 //(regard_warnings != MagickFalse))
836 return; /* FATAL - caller handles exception */
837 CatchException(wand->exception); /* output warnings and clear!!! */
841 if ( ( process_flags & ProcessOutputFile ) == 0 )
846 Write out final image!
850 #if MagickCommandDebug
851 (void) FormatLocaleFile(stderr, "CLI Output: \"%s\"\n", option );
854 // if stacks are not empty
855 // ThrowConvertException(OptionError,"UnbalancedParenthesis",option,i);
857 /* This is a valid 'do no write' option for a CLI */
858 if (LocaleCompare(option,"-exit") == 0 )
859 return; /* just exit, no image write */
861 /* If there is an option -- produce an error */
862 if (IsCommandOption(option) != MagickFalse)
863 /* FUTURE: Better Error - Output Filename not Found */
864 MagickExceptionReturn(OptionError,"MissingOutputFilename",option,i);
866 /* If no images in MagickWand */
867 if ( wand->images == (Image *) NULL )
869 /* a "null:" output coder with no images is not an error! */
870 if ( LocaleCompare(option,"null:") == 0 )
872 MagickExceptionReturn(OptionError,"NoImagesForFinalWrite",option,i);
875 //WandListOperatorImages(wand,MagickFalse,"write",option,(const char *)NULL);
876 (void) SyncImagesSettings(wand->image_info,wand->images,wand->exception);
877 (void) WriteImages(wand->image_info,wand->images,option,wand->exception);
883 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
887 + M a g i c k I m a g e C o m m a n d %
891 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
893 % MagickImageCommand() Handle special use CLI arguments and prepare a
894 % CLI MagickWand to process the command line or directly specified script.
896 % This is essentualy interface function between the MagickCore library
897 % initialization function MagickCommandGenesis(), and the option MagickWand
898 % processing functions ProcessCommandOptions() or ProcessScriptOptions()
900 % The format of the MagickImageCommand method is:
902 % MagickBooleanType MagickImageCommand(ImageInfo *image_info,
903 % int argc, char **argv, char **metadata, ExceptionInfo *exception)
905 % A description of each parameter follows:
907 % o image_info: the starting image_info structure
908 % (for compatibilty with MagickCommandGenisis())
910 % o argc: the number of elements in the argument vector.
912 % o argv: A text array containing the command line arguments.
914 % o metadata: any metadata is returned here.
915 % (for compatibilty with MagickCommandGenisis())
917 % o exception: return any errors or warnings in this structure.
921 static MagickBooleanType MagickUsage(void)
923 printf("Version: %s\n",GetMagickVersion((size_t *) NULL));
924 printf("Copyright: %s\n",GetMagickCopyright());
925 printf("Features: %s\n\n",GetMagickFeatures());
928 printf("Usage: %s [(options|images) ...] output_image\n", GetClientName());
929 printf(" %s -script filename [script args...]\n", GetClientName());
930 printf(" ... | %s -script - | ...\n", GetClientName());
933 printf(" For more information on usage, options, examples, and technqiues\n");
934 printf(" see the ImageMagick website at\n %s\n", MagickAuthoritativeURL);
935 printf(" Or the web pages in ImageMagick Sources\n");
940 Concatanate given file arguments to the given output argument.
941 Used for a special -concatenate option used for specific 'delegates'.
942 The option is not formally documented.
944 magick -concatenate files... output
946 This is much like the UNIX "cat" command, but for both UNIX and Windows,
947 however the last argument provides the output filename.
949 #define ThrowFileException(exception,severity,tag,context) \
954 message=GetExceptionMessage(errno); \
955 (void) ThrowMagickException(exception,GetMagickModule(),severity, \
956 tag == (const char *) NULL ? "unknown" : tag,"`%s': %s",context,message); \
957 message=DestroyString(message); \
960 static MagickBooleanType ConcatenateImages(int argc,char **argv,
961 ExceptionInfo *exception)
973 output=fopen_utf8(argv[argc-1],"wb");
974 if (output == (FILE *) NULL)
976 ThrowFileException(exception,FileOpenError,"UnableToOpenFile",
980 for (i=2; i < (ssize_t) (argc-1); i++)
982 input=fopen_utf8(argv[i],"rb");
983 if (input == (FILE *) NULL)
984 ThrowFileException(exception,FileOpenError,"UnableToOpenFile",argv[i]);
985 for (c=fgetc(input); c != EOF; c=fgetc(input))
986 (void) fputc((char) c,output);
987 (void) fclose(input);
988 (void) remove_utf8(argv[i]);
990 (void) fclose(output);
994 WandExport MagickBooleanType MagickImageCommand(ImageInfo *image_info,
995 int argc,char **argv,char **metadata,ExceptionInfo *exception)
1003 /* Handle special single use options */
1007 if ((LocaleCompare("-version",option+1) == 0) ||
1008 (LocaleCompare("--version",option+1) == 0) )
1010 (void) FormatLocaleFile(stdout,"Version: %s\n",
1011 GetMagickVersion((size_t *) NULL));
1012 (void) FormatLocaleFile(stdout,"Copyright: %s\n",
1013 GetMagickCopyright());
1014 (void) FormatLocaleFile(stdout,"Features: %s\n\n",
1015 GetMagickFeatures());
1016 return(MagickFalse);
1019 /* The "magick" command must have at least two arguments */
1021 return(MagickUsage());
1022 ReadCommandlLine(argc,&argv);
1025 /* FUTURE: This does not make sense! Remove it.
1026 Only implied 'image read' needs to expand file name glob patterns
1028 status=ExpandFilenames(&argc,&argv);
1029 if (status == MagickFalse)
1030 ThrowConvertException(ResourceLimitError,"MemoryAllocationFailed",
1031 GetExceptionMessage(errno));
1034 /* Special hidden option for 'delegates' - no wand needed */
1035 if (LocaleCompare("-concatenate",argv[1]) == 0)
1036 return(ConcatenateImages(argc,argv,exception));
1038 /* Initialize special "CLI Wand" to hold images and settings (empty) */
1039 /* FUTURE: add this to 'operations.c' */
1040 wand=NewMagickWand();
1041 wand->image_info=DestroyImageInfo(wand->image_info);
1042 wand->image_info=image_info;
1043 wand->exception=DestroyExceptionInfo(wand->exception);
1044 wand->exception=exception;
1045 wand->draw_info=CloneDrawInfo(image_info,(DrawInfo *) NULL);
1046 wand->quantize_info=AcquireQuantizeInfo(image_info);
1048 if (LocaleCompare("-list",argv[1]) == 0)
1049 /* Special option - list argument constants and other information */
1050 /* FUTURE - this really should be a direct MagickCore Function */
1051 WandSettingOptionInfo(wand, argv[1]+1, argv[2]);
1052 else if (LocaleCompare("-script",argv[1]) == 0)
1054 /* Start processing from script, no pre-script options */
1055 GetPathComponent(argv[2],TailPath,wand->name);
1056 ProcessScriptOptions(wand,argc-2,argv+2);
1060 /* Processing Command line, assuming output file as last option */
1061 ProcessCommandOptions(wand,argc-1,argv+1,MagickCommandOptionFlags);
1064 assert(wand->exception == exception);
1065 assert(wand->image_info == image_info);
1067 /* Handle metadata for ImageMagickObject COM object for Windows VBS */
1068 if (metadata != (char **) NULL)
1076 format="%w,%h,%m"; // Get this from image_info Option splaytree
1078 text=InterpretImageProperties(image_info,wand->images,format,exception);
1079 if (text == (char *) NULL)
1080 ThrowMagickException(exception,GetMagickModule(),ResourceLimitError,
1081 "MemoryAllocationFailed","`%s'", GetExceptionMessage(errno));
1084 (void) ConcatenateString(&(*metadata),text);
1085 text=DestroyString(text);
1089 /* Destroy the special CLI Wand */
1090 wand->exception = (ExceptionInfo *)NULL;
1091 wand->image_info = (ImageInfo *)NULL;
1092 wand=DestroyMagickWand(wand);
1094 return((exception->severity > ErrorException) ? MagickFalse : MagickTrue);