49#include "MagickWand/studio.h"
50#include "MagickWand/MagickWand.h"
51#include "MagickWand/magick-wand-private.h"
52#include "MagickWand/wandcli.h"
53#include "MagickWand/wandcli-private.h"
54#include "MagickWand/operation.h"
55#include "MagickWand/magick-cli.h"
56#include "MagickWand/script-token.h"
57#include "MagickCore/string-private.h"
58#include "MagickCore/thread-private.h"
59#include "MagickCore/utility-private.h"
60#include "MagickCore/exception-private.h"
61#include "MagickCore/version.h"
68#define MagickCommandDebug 0
116WandExport MagickBooleanType MagickCommandGenesis(ImageInfo *image_info,
117 MagickCommand command,
int argc,
char **argv,
char **metadata,
118 ExceptionInfo *exception)
121 client_name[MagickPathExtent],
141 (void) setlocale(LC_ALL,
"");
142 (void) setlocale(LC_NUMERIC,
"C");
143 GetPathComponent(argv[0],TailPath,client_name);
144 (void) SetClientName(client_name);
145 concurrent=MagickFalse;
149 regard_warnings=MagickFalse;
150 for (i=1; i < (ssize_t) (argc-1); i++)
153 if ((strlen(option) == 1) || ((*option !=
'-') && (*option !=
'+')))
155 if (LocaleCompare(
"-bench",option) == 0)
156 iterations=StringToUnsignedLong(argv[++i]);
157 if (LocaleCompare(
"-concurrent",option) == 0)
158 concurrent=MagickTrue;
159 if (LocaleCompare(
"-debug",option) == 0)
160 (void) SetLogEventMask(argv[++i]);
161 if (LocaleCompare(
"-distribute-cache",option) == 0)
163 DistributePixelCacheServer(StringToInteger(argv[++i]),exception);
166 if (LocaleCompare(
"-duration",option) == 0)
167 duration=StringToDouble(argv[++i],(
char **) NULL);
168 if (LocaleCompare(
"-regard-warnings",option) == 0)
169 regard_warnings=MagickTrue;
177 status=command(image_info,argc,argv,&text,exception);
178 if (exception->severity != UndefinedException)
180 if ((exception->severity > ErrorException) ||
181 (regard_warnings != MagickFalse))
183 CatchException(exception);
185 if (text != (
char *) NULL)
187 if (metadata != (
char **) NULL)
188 (void) ConcatenateString(&(*metadata),text);
189 text=DestroyString(text);
193 number_threads=GetOpenMPMaximumThreads();
195 for (n=1; n <= (ssize_t) number_threads; n++)
205 (void) SetMagickResourceLimit(ThreadResource,(MagickSizeType) n);
206 timer=AcquireTimerInfo();
207 if (concurrent == MagickFalse)
209 for (i=0; i < (ssize_t) iterations; i++)
215 if (status == MagickFalse)
219 if (GetElapsedTime(timer) > duration)
221 (void) ContinueTimer(timer);
223 status=command(image_info,argc,argv,&text,exception);
224 if (exception->severity != UndefinedException)
226 if ((exception->severity > ErrorException) ||
227 (regard_warnings != MagickFalse))
229 CatchException(exception);
231 if (text != (
char *) NULL)
233 if (metadata != (
char **) NULL)
234 (void) ConcatenateString(&(*metadata),text);
235 text=DestroyString(text);
242#if defined(MAGICKCORE_OPENMP_SUPPORT)
243 # pragma omp parallel for shared(status)
245 for (i=0; i < (ssize_t) iterations; i++)
251 if (status == MagickFalse)
255 if (GetElapsedTime(timer) > duration)
257 (void) ContinueTimer(timer);
259 status=command(image_info,argc,argv,&text,exception);
260#if defined(MAGICKCORE_OPENMP_SUPPORT)
261 # pragma omp critical (MagickCore_MagickCommandGenesis)
264 if (exception->severity != UndefinedException)
266 if ((exception->severity > ErrorException) ||
267 (regard_warnings != MagickFalse))
269 CatchException(exception);
271 if (text != (
char *) NULL)
273 if (metadata != (
char **) NULL)
274 (void) ConcatenateString(&(*metadata),text);
275 text=DestroyString(text);
280 user_time=GetUserTime(timer);
281 parallel=GetElapsedTime(timer);
286 e=((1.0/(1.0/((serial/(serial+parallel))+(1.0-(serial/(serial+parallel)))/
287 (double) n)))-(1.0/(double) n))/(1.0-1.0/(
double) n);
288 (void) FormatLocaleFile(stderr,
289 " Performance[%.20g]: %.20gi %0.3fips %0.6fe %0.6fu %lu:%02lu.%03lu\n",
290 (
double) n,(
double) iterations,(
double) iterations/parallel,e,user_time,
291 (
unsigned long) (parallel/60.0),(
unsigned long) floor(fmod(parallel,
292 60.0)),(
unsigned long) (1000.0*(parallel-floor(parallel))+0.5));
293 timer=DestroyTimerInfo(timer);
340WandExport
void ProcessScriptOptions(
MagickCLI *cli_wand,
const char *filename,
341 int magick_unused(argc),
char **magick_unused(argv),
int magick_unused(index))
357 magick_unreferenced(argc);
358 magick_unreferenced(argv);
359 magick_unreferenced(index);
360 assert(filename != (
char *) NULL );
362 assert(cli_wand->signature == MagickWandSignature);
363 if (cli_wand->wand.debug != MagickFalse)
364 (void) LogMagickEvent(CommandEvent,GetMagickModule(),
365 "Processing script \"%s\"", filename);
368 token_info = AcquireScriptTokenInfo(filename);
370 CLIWandExceptionFile(OptionFatalError,
"UnableToOpenScript",filename);
376 cli_wand->location=
"in \"%s\" at line %u,column %u";
377 if ( LocaleCompare(
"-", filename) == 0 )
378 cli_wand->filename=
"stdin";
380 cli_wand->filename=filename;
383 option = arg1 = arg2 = (
char*) NULL;
384DisableMSCWarning(4127)
388 { MagickBooleanType status = GetScriptToken(token_info);
389 cli_wand->line=token_info->token_line;
390 cli_wand->column=token_info->token_column;
391 if (status == MagickFalse)
398 CloneString(&option,token_info->token);
401 cli_wand->command = GetCommandOptionInfo(option);
402 count=cli_wand->command->type;
403 option_type=(CommandOptionFlags) cli_wand->command->flags;
405 (void) FormatLocaleFile(stderr,
"Script: %u,%u: \"%s\" matched \"%s\"\n",
406 cli_wand->line, cli_wand->line, option, cli_wand->command->mnemonic );
410 if ( option_type == UndefinedOptionFlag ||
411 (option_type & NonMagickOptionFlag) != 0 ) {
412#if MagickCommandDebug >= 3
413 (void) FormatLocaleFile(stderr,
"Script %u,%u Non-Option: \"%s\"\n",
414 cli_wand->line, cli_wand->line, option);
416 if (IsCommandOption(option) == MagickFalse) {
418 cli_wand->command=(
const OptionInfo *) NULL;
419 CLIOption(cli_wand,
"-read",option);
422 CLIWandException(OptionFatalError,
"UnrecognizedOption",option);
427 if (GetScriptToken(token_info) == MagickFalse)
428 CLIWandException(OptionFatalError,
"MissingArgument",option);
429 CloneString(&arg1,token_info->token);
432 CloneString(&arg1,(
char *) NULL);
435 if (GetScriptToken(token_info) == MagickFalse)
436 CLIWandExceptionBreak(OptionFatalError,
"MissingArgument",option);
437 CloneString(&arg2,token_info->token);
440 CloneString(&arg2,(
char *) NULL);
445#if MagickCommandDebug >= 3
446 (void) FormatLocaleFile(stderr,
447 "Script %u,%u Option: \"%s\" Count: %d Flags: %04x Args: \"%s\" \"%s\"\n",
448 cli_wand->line,cli_wand->line,option,count,option_type,arg1,arg2);
451 if ( (option_type & DeprecateOptionFlag) != 0 ) {
452 CLIWandException(OptionError,
"DeprecatedOptionNoCode",option);
457 if ( (option_type & GenesisOptionFlag) != 0 ) {
458 CLIWandException(OptionError,
"InvalidUseOfOption",option);
463 if ( (option_type & SpecialOptionFlag) != 0 ) {
464 if ( LocaleCompare(option,
"-exit") == 0 ) {
467 if ( LocaleCompare(option,
"-script") == 0 ) {
469 CLIWandException(OptionError,
"InvalidUseOfOption",option);
474 CLIWandException(OptionError,
"InvalidUseOfOption",option);
479 CLIOption(cli_wand, option, arg1, arg2);
480 (void) fflush(stdout);
481 (void) fflush(stderr);
483DisableMSCWarning(4127)
487#if MagickCommandDebug >= 5
488 fprintf(stderr,
"Script Image Count = %ld\n",
489 GetImageListLength(cli_wand->wand.images) );
491 if (CLICatchException(cli_wand, MagickFalse) != MagickFalse)
499#if MagickCommandDebug >= 3
500 (void) FormatLocaleFile(stderr,
"Script End: %d\n", token_info->status);
502 switch( token_info->status ) {
505 if (cli_wand->image_list_stack != (
CLIStack *) NULL)
506 CLIWandException(OptionError,
"UnbalancedParenthesis",
"(eof)");
507 else if (cli_wand->image_info_stack != (
CLIStack *) NULL)
508 CLIWandException(OptionError,
"UnbalancedBraces",
"(eof)");
510 case TokenStatusBadQuotes:
512 if( strlen(token_info->token) > INITAL_TOKEN_LENGTH-1 ) {
513 token_info->token[INITAL_TOKEN_LENGTH-4] =
'.';
514 token_info->token[INITAL_TOKEN_LENGTH-3] =
'.';
515 token_info->token[INITAL_TOKEN_LENGTH-2] =
'.';
516 token_info->token[INITAL_TOKEN_LENGTH-1] =
'\0';
518 CLIWandException(OptionFatalError,
"ScriptUnbalancedQuotes",
521 case TokenStatusMemoryFailed:
522 CLIWandException(OptionFatalError,
"ScriptTokenMemoryFailed",
"");
524 case TokenStatusBinary:
525 CLIWandException(OptionFatalError,
"ScriptIsBinary",
"");
528 (void) fflush(stdout);
529 (void) fflush(stderr);
530 if (cli_wand->wand.debug != MagickFalse)
531 (void) LogMagickEvent(CommandEvent,GetMagickModule(),
532 "Script End \"%s\"", filename);
535 token_info = DestroyScriptTokenInfo(token_info);
537 CloneString(&option,(
char *) NULL);
538 CloneString(&arg1,(
char *) NULL);
539 CloneString(&arg2,(
char *) NULL);
587WandExport
int ProcessCommandOptions(
MagickCLI *cli_wand,
int argc,
char **argv,
604 assert(argv != (
char **) NULL);
605 assert(argv[index] != (
char *) NULL);
606 assert(argv[argc-1] != (
char *) NULL);
608 assert(cli_wand->signature == MagickWandSignature);
612 cli_wand->location=
"at %s arg %u";
613 cli_wand->filename=
"CLI";
614 cli_wand->line=(size_t) index;
616 if (cli_wand->wand.debug != MagickFalse)
617 (void) CLILogEvent(cli_wand,CommandEvent,GetMagickModule(),
618 "- Starting (\"%s\")", argv[index]);
621 if ( (cli_wand->process_flags & ProcessImplicitWrite) != 0 )
624 for (i=index; i < end; i += count +1) {
626 if ( (cli_wand->process_flags & ProcessOneOptionOnly) != 0 && i != index )
632 cli_wand->line=(size_t) i;
635 cli_wand->command = GetCommandOptionInfo(argv[i]);
636 count=cli_wand->command->type;
637 option_type=(CommandOptionFlags) cli_wand->command->flags;
639 (void) FormatLocaleFile(stderr,
"CLI %d: \"%s\" matched \"%s\"\n",
640 i, argv[i], cli_wand->command->mnemonic );
643 if ( option_type == UndefinedOptionFlag ||
644 (option_type & NonMagickOptionFlag) != 0 ) {
645#if MagickCommandDebug >= 3
646 (void) FormatLocaleFile(stderr,
"CLI arg %d Non-Option: \"%s\"\n",
649 if (IsCommandOption(option) == MagickFalse) {
650 if ( (cli_wand->process_flags & ProcessImplicitRead) != 0 ) {
652 cli_wand->command=(
const OptionInfo *) NULL;
653 CLIOption(cli_wand,
"-read",option);
657 CLIWandException(OptionFatalError,
"UnrecognizedOption",option);
661 if ( ((option_type & SpecialOptionFlag) != 0 ) &&
662 ((cli_wand->process_flags & ProcessScriptOption) != 0) &&
663 (LocaleCompare(option,
"-script") == 0) ) {
668 if ( (i+count) >= argc )
669 CLIWandException(OptionFatalError,
"MissingArgument",option);
670 ProcessScriptOptions(cli_wand,argv[i+1],argc,argv,i+count);
675 if ((i+count) >= end ) {
676 CLIWandException(OptionFatalError,
"MissingArgument",option);
677 if ( CLICatchException(cli_wand, MagickFalse) != MagickFalse )
682 arg1 = ( count >= 1 ) ? argv[i+1] : (char *) NULL;
683 arg2 = ( count >= 2 ) ? argv[i+2] : (
char *) NULL;
688#if MagickCommandDebug >= 3
689 (void) FormatLocaleFile(stderr,
690 "CLI arg %u Option: \"%s\" Count: %d Flags: %04x Args: \"%s\" \"%s\"\n",
691 i,option,count,option_type,arg1,arg2);
694 if ( (option_type & GenesisOptionFlag) != 0 )
698 if ( (option_type & SpecialOptionFlag) != 0 ) {
699 if ( (cli_wand->process_flags & ProcessExitOption) != 0
700 && LocaleCompare(option,
"-exit") == 0 )
706 CLIOption(cli_wand, option, arg1, arg2);
708DisableMSCWarning(4127)
712#if MagickCommandDebug >= 5
713 (void) FormatLocaleFile(stderr,
"CLI-post Image Count = %ld\n",
714 (
long) GetImageListLength(cli_wand->wand.images) );
716 if ( CLICatchException(cli_wand, MagickFalse) != MagickFalse )
721 if ( (cli_wand->process_flags & ProcessImplicitWrite) == 0 )
730 cli_wand->line=(size_t) i;
733 if (cli_wand->image_list_stack != (
CLIStack *) NULL)
734 CLIWandException(OptionError,
"UnbalancedParenthesis",
"(end of cli)");
735 else if (cli_wand->image_info_stack != (
CLIStack *) NULL)
736 CLIWandException(OptionError,
"UnbalancedBraces",
"(end of cli)");
737 if ( CLICatchException(cli_wand, MagickFalse) != MagickFalse )
740#if MagickCommandDebug >= 3
741 (void) FormatLocaleFile(stderr,
"CLI arg %d Write File: \"%s\"\n",i,option);
745 if (LocaleCompare(option,
"-exit") == 0 )
751 if (IsCommandOption(option) != MagickFalse ||
752 (option[0] ==
' ' && option[1] ==
'\0') ) {
753 CLIWandException(OptionError,
"MissingOutputFilename",option);
757 cli_wand->command=(
const OptionInfo *) NULL;
758 CLIOption(cli_wand,
"-write",option);
801static void MagickUsage(MagickBooleanType verbose)
809 name=GetClientName();
812 if (len>=7 && LocaleCompare(
"convert",name+len-7) == 0) {
814 (void) FormatLocaleFile(stdout,
815 "Usage: %s [ {option} | {image} ... ] {output_image}\n",name);
816 (void) FormatLocaleFile(stdout,
817 " %s -help | -version | -usage | -list {option}\n\n",name);
820 else if (len>=6 && LocaleCompare(
"script",name+len-6) == 0) {
822 (void) FormatLocaleFile(stdout,
823 "Usage: %s {filename} [ {script_args} ... ]\n",name);
827 (void) FormatLocaleFile(stdout,
828 "Usage: %s tool [ {option} | {image} ... ] {output_image}\n",name);
829 (void) FormatLocaleFile(stdout,
830 "Usage: %s [ {option} | {image} ... ] {output_image}\n",name);
831 (void) FormatLocaleFile(stdout,
832 " %s [ {option} | {image} ... ] -script {filename} [ {script_args} ...]\n",
835 (void) FormatLocaleFile(stdout,
836 " %s -help | -version | -usage | -list {option}\n\n",name);
838 if (verbose == MagickFalse)
841 (void) FormatLocaleFile(stdout,
"%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
842 "All options are performed in a strict 'as you see them' order\n",
843 "You must read-in images before you can operate on them.\n",
845 "Magick Script files can use any of the following forms...\n",
846 " #!/path/to/magick -script\n",
849 " :; exec magick -script \"$0\" \"$@\"; exit 10\n",
850 " # Magick script from here...\n",
852 " #!/usr/bin/env magick-script\n",
853 "The latter two forms do not require the path to the command hard coded.\n",
854 "Note: \"magick-script\" needs to be linked to the \"magick\" command.\n",
856 "For more information on usage, options, examples, and techniques\n",
857 "see the ImageMagick website at ", MagickAuthoritativeURL);
872static MagickBooleanType ConcatenateImages(
int argc,
char **argv,
873 ExceptionInfo *exception )
888 if (ExpandFilenames(&argc,&argv) == MagickFalse)
889 ThrowFileException(exception,ResourceLimitError,
"MemoryAllocationFailed",
890 GetExceptionMessage(errno));
891 output=fopen_utf8(argv[argc-1],
"wb");
892 if (output == (FILE *) NULL)
894 ThrowFileException(exception,FileOpenError,
"UnableToOpenFile",
899 for (i=2; i < (ssize_t) (argc-1); i++)
901 input=fopen_utf8(argv[i],
"rb");
902 if (input == (FILE *) NULL)
904 ThrowFileException(exception,FileOpenError,
"UnableToOpenFile",argv[i]);
907 for (c=fgetc(input); c != EOF; c=fgetc(input))
908 if (fputc((
char) c,output) != c)
910 (void) fclose(input);
911 (void) remove_utf8(argv[i]);
913 (void) fclose(output);
917WandExport MagickBooleanType MagickImageCommand(ImageInfo *image_info,
int argc,
918 char **argv,
char **metadata,ExceptionInfo *exception)
926 assert(image_info != (ImageInfo *) NULL);
929 ReadCommandlLine(argc,&argv);
932 cli_wand=AcquireMagickCLI(image_info,exception);
933 cli_wand->location=
"Initializing";
934 cli_wand->filename=argv[0];
937 if (cli_wand->wand.debug != MagickFalse)
938 (void) CLILogEvent(cli_wand,CommandEvent,GetMagickModule(),
942 GetPathComponent(argv[0],TailPath,cli_wand->wand.name);
943 SetClientName(cli_wand->wand.name);
944 ConcatenateMagickString(cli_wand->wand.name,
"-CLI",MagickPathExtent);
949 if (len>=7 && LocaleCompare(
"convert",argv[0]+len-7) == 0) {
950 cli_wand->process_flags = ConvertCommandOptionFlags;
951 (void) FormatLocaleFile(stderr,
"WARNING: %s\n",
952 "The convert command is deprecated in IMv7, use \"magick\"\n");
956 if (len>=6 && LocaleCompare(
"script",argv[0]+len-6) == 0) {
957 if (argc >= 2 && ( (*(argv[1]) !=
'-') || (strlen(argv[1]) == 1) )) {
958 GetPathComponent(argv[1],TailPath,cli_wand->wand.name);
959 ProcessScriptOptions(cli_wand,argv[1],argc,argv,2);
960 goto Magick_Command_Cleanup;
966 if ((LocaleCompare(
"-version",argv[1]) == 0) ||
967 (LocaleCompare(
"--version",argv[1]) == 0) ) {
968 CLIOption(cli_wand,
"-version");
969 goto Magick_Command_Exit;
971 if ((LocaleCompare(
"-help",argv[1]) == 0) ||
972 (LocaleCompare(
"--help",argv[1]) == 0) ) {
973 if (cli_wand->wand.debug != MagickFalse)
974 (void) CLILogEvent(cli_wand,CommandEvent,GetMagickModule(),
975 "- Special Option \"%s\"", argv[1]);
976 MagickUsage(MagickFalse);
977 goto Magick_Command_Exit;
979 if (LocaleCompare(
"-usage",argv[1]) == 0) {
980 if (cli_wand->wand.debug != MagickFalse)
981 (void) CLILogEvent(cli_wand,CommandEvent,GetMagickModule(),
982 "- Special Option \"%s\"", argv[1]);
983 CLIOption(cli_wand,
"-version" );
984 MagickUsage(MagickTrue);
985 goto Magick_Command_Exit;
991 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
992 "InvalidArgument",
"%s",argc > 1 ? argv[argc-1] :
"");
993 MagickUsage(MagickFalse);
994 goto Magick_Command_Exit;
998 if (LocaleCompare(
"-concatenate",argv[1]) == 0) {
999 if (cli_wand->wand.debug != MagickFalse)
1000 (void) CLILogEvent(cli_wand,CommandEvent,GetMagickModule(),
1001 "- Special Option \"%s\"", argv[1]);
1002 ConcatenateImages(argc,argv,exception);
1003 goto Magick_Command_Exit;
1007 if (argc == 3 && LocaleCompare(
"-list",argv[1]) == 0) {
1008 CLIOption(cli_wand, argv[1], argv[2]);
1009 goto Magick_Command_Exit;
1015 if (LocaleCompare(
"-script",argv[1]) == 0) {
1020 GetPathComponent(argv[2],TailPath,cli_wand->wand.name);
1021 ProcessScriptOptions(cli_wand,argv[2],argc,argv,3);
1025 ProcessCommandOptions(cli_wand,argc,argv,1);
1029Magick_Command_Cleanup:
1030 cli_wand->location=
"Cleanup";
1031 cli_wand->filename=argv[0];
1032 if (cli_wand->wand.debug != MagickFalse)
1033 (void) CLILogEvent(cli_wand,CommandEvent,GetMagickModule(),
1038 while ((cli_wand->image_list_stack != (
CLIStack *) NULL) &&
1039 (cli_wand->image_list_stack->next != (
CLIStack *) NULL))
1040 CLIOption(cli_wand,
")");
1041 while ((cli_wand->image_info_stack != (
CLIStack *) NULL) &&
1042 (cli_wand->image_info_stack->next != (
CLIStack *) NULL))
1043 CLIOption(cli_wand,
"}");
1046 assert(cli_wand->wand.image_info == image_info);
1047 assert(cli_wand->wand.exception == exception);
1050 if ((cli_wand->wand.images != (Image *) NULL) &&
1051 (metadata != (
char **) NULL))
1060 text=InterpretImageProperties(image_info,cli_wand->wand.images,format,
1062 if (text == (
char *) NULL)
1063 ThrowMagickException(exception,GetMagickModule(),ResourceLimitError,
1064 "MemoryAllocationFailed",
"`%s'", GetExceptionMessage(errno));
1067 (void) ConcatenateString(&(*metadata),text);
1068 text=DestroyString(text);
1073 cli_wand->location=
"Exiting";
1074 cli_wand->filename=argv[0];
1075 if (cli_wand->wand.debug != MagickFalse)
1076 (void) CLILogEvent(cli_wand,CommandEvent,GetMagickModule(),
1080 cli_wand->wand.image_info = (ImageInfo *) NULL;
1081 cli_wand->wand.exception = (ExceptionInfo *) NULL;
1082 cli_wand=DestroyMagickCLI(cli_wand);
1084 return(exception->severity < ErrorException ? MagickTrue : MagickFalse);