1/* 2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3% % 4% % 5% % 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 % 11% % 12% CCCC L IIIII % 13% C L I % 14% C L I % 15% C L I % 16% CCCC LLLLL IIIII % 17% % 18% Perform "Magick" on Images via the Command Line Interface % 19% % 20% Dragon Computing % 21% Anthony Thyssen % 22% January 2012 % 23% % 24% % 25% Copyright 1999-2016 ImageMagick Studio LLC, a non-profit organization % 26% dedicated to making software imaging solutions freely available. % 27% % 28% You may not use this file except in compliance with the License. You may % 29% obtain a copy of the License at % 30% % 31% http://www.imagemagick.org/script/license.php % 32% % 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. % 38% % 39%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 40% 41% Read CLI arguments, script files, and pipelines, to provide options that 42% manipulate images from many different formats. 43% 44*/ 45 46/* 47 Include declarations. 48*/ 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/utility-private.h" 58#include "MagickCore/exception-private.h" 59#include "MagickCore/version.h" 60 61/* verbose debugging, 62 0 - no debug lines 63 3 - show option details (better to use -debug Command now) 64 5 - image counts (after option runs) 65*/ 66#define MagickCommandDebug 0 67 68 69/* 70%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 71% % 72% % 73% % 74+ P r o c e s s S c r i p t O p t i o n s % 75% % 76% % 77% % 78%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 79% 80% ProcessScriptOptions() reads options and processes options as they are 81% found in the given file, or pipeline. The filename to open and read 82% options is given as the 'index' argument of the argument array given. 83% 84% Other arguments following index may be read by special script options 85% as settings (strings), images, or as operations to be processed in various 86% ways. How they are treated is up to the script being processed. 87% 88% Note that a script not 'return' to the command line processing, nor can 89% they call (and return from) other scripts. At least not at this time. 90% 91% There are no 'ProcessOptionFlags' control flags at this time. 92% 93% The format of the ProcessScriptOptions method is: 94% 95% void ProcessScriptOptions(MagickCLI *cli_wand,const char *filename, 96% int argc,char **argv,int index) 97% 98% A description of each parameter follows: 99% 100% o cli_wand: the main CLI Wand to use. 101% 102% o filename: the filename of script to process 103% 104% o argc: the number of elements in the argument vector. (optional) 105% 106% o argv: A text array containing the command line arguments. (optional) 107% 108% o index: offset of next argment in argv (script arguments) (optional) 109% 110*/ 111WandExport void ProcessScriptOptions(MagickCLI *cli_wand,const char *filename, 112 int argc,char **argv,int index) 113{ 114 ScriptTokenInfo 115 *token_info; 116 117 CommandOptionFlags 118 option_type; 119 120 int 121 count; 122 123 char 124 *option, 125 *arg1, 126 *arg2; 127 128 assert(filename != (char *) NULL ); /* at least one argument - script name */ 129 assert(cli_wand != (MagickCLI *) NULL); 130 assert(cli_wand->signature == MagickWandSignature); 131 if (cli_wand->wand.debug != MagickFalse) 132 (void) LogMagickEvent(CommandEvent,GetMagickModule(), 133 "Processing script \"%s\"", filename); 134 135 /* open file script or stream, and set up tokenizer */ 136 token_info = AcquireScriptTokenInfo(filename); 137 if (token_info == (ScriptTokenInfo *) NULL) { 138 CLIWandExceptionFile(OptionFatalError,"UnableToOpenScript",filename); 139 return; 140 } 141 142 /* define the error location string for use in exceptions 143 order of localtion format escapes: filename, line, column */ 144 cli_wand->location="in \"%s\" at line %u,column %u"; 145 if ( LocaleCompare("-", filename) == 0 ) 146 cli_wand->filename="stdin"; 147 else 148 cli_wand->filename=filename; 149 150 /* Process Options from Script */ 151 option = arg1 = arg2 = (char*) NULL; 152DisableMSCWarning(4127) 153 while (1) { 154RestoreMSCWarning 155 156 { MagickBooleanType status = GetScriptToken(token_info); 157 cli_wand->line=token_info->token_line; 158 cli_wand->column=token_info->token_column; 159 if (status == MagickFalse) 160 break; /* error or end of options */ 161 } 162 163 do { /* use break to loop to exception handler and loop */ 164 165 /* save option details */ 166 CloneString(&option,token_info->token); 167 168 /* get option, its argument count, and option type */ 169 cli_wand->command = GetCommandOptionInfo(option); 170 count=cli_wand->command->type; 171 option_type=(CommandOptionFlags) cli_wand->command->flags; 172#if 0 173 (void) FormatLocaleFile(stderr, "Script: %u,%u: \"%s\" matched \"%s\"\n", 174 cli_wand->line, cli_wand->line, option, cli_wand->command->mnemonic ); 175#endif 176 177 /* handle a undefined option - image read - always for "magick-script" */ 178 if ( option_type == UndefinedOptionFlag || 179 (option_type & NonMagickOptionFlag) != 0 ) { 180#if MagickCommandDebug >= 3 181 (void) FormatLocaleFile(stderr, "Script %u,%u Non-Option: \"%s\"\n", 182 cli_wand->line, cli_wand->line, option); 183#endif 184 if (IsCommandOption(option) == MagickFalse) { 185 /* non-option -- treat as a image read */ 186 cli_wand->command=(const OptionInfo *) NULL; 187 CLIOption(cli_wand,"-read",option); 188 break; /* next option */ 189 } 190 CLIWandException(OptionFatalError,"UnrecognizedOption",option); 191 break; /* next option */ 192 } 193 194 if ( count >= 1 ) { 195 if (GetScriptToken(token_info) == MagickFalse) 196 CLIWandException(OptionFatalError,"MissingArgument",option); 197 CloneString(&arg1,token_info->token); 198 } 199 else 200 CloneString(&arg1,(char *) NULL); 201 202 if ( count >= 2 ) { 203 if (GetScriptToken(token_info) == MagickFalse) 204 CLIWandExceptionBreak(OptionFatalError,"MissingArgument",option); 205 CloneString(&arg2,token_info->token); 206 } 207 else 208 CloneString(&arg2,(char *) NULL); 209 210 /* 211 Process Options 212 */ 213#if MagickCommandDebug >= 3 214 (void) FormatLocaleFile(stderr, 215 "Script %u,%u Option: \"%s\" Count: %d Flags: %04x Args: \"%s\" \"%s\"\n", 216 cli_wand->line,cli_wand->line,option,count,option_type,arg1,arg2); 217#endif 218 /* Hard Deprecated Options, no code to execute - error */ 219 if ( (option_type & DeprecateOptionFlag) != 0 ) { 220 CLIWandException(OptionError,"DeprecatedOptionNoCode",option); 221 break; /* next option */ 222 } 223 224 /* MagickCommandGenesis() options have no place in a magick script */ 225 if ( (option_type & GenesisOptionFlag) != 0 ) { 226 CLIWandException(OptionError,"InvalidUseOfOption",option); 227 break; /* next option */ 228 } 229 230 /* handle any special 'script' options */ 231 if ( (option_type & SpecialOptionFlag) != 0 ) { 232 if ( LocaleCompare(option,"-exit") == 0 ) { 233 goto loop_exit; /* break out of loop - return from script */ 234 } 235 if ( LocaleCompare(option,"-script") == 0 ) { 236 /* FUTURE: call new script from this script - error for now */ 237 CLIWandException(OptionError,"InvalidUseOfOption",option); 238 break; /* next option */ 239 } 240 /* FUTURE: handle special script-argument options here */ 241 /* handle any other special operators now */ 242 CLIWandException(OptionError,"InvalidUseOfOption",option); 243 break; /* next option */ 244 } 245 246 /* Process non-specific Option */ 247 CLIOption(cli_wand, option, arg1, arg2); 248 (void) fflush(stdout); 249 (void) fflush(stderr); 250 251DisableMSCWarning(4127) 252 } while (0); /* break block to next option */ 253RestoreMSCWarning 254 255#if MagickCommandDebug >= 5 256 fprintf(stderr, "Script Image Count = %ld\n", 257 GetImageListLength(cli_wand->wand.images) ); 258#endif 259 if (CLICatchException(cli_wand, MagickFalse) != MagickFalse) 260 break; /* exit loop */ 261 } 262 263 /* 264 Loop exit - check for some tokenization error 265 */ 266loop_exit: 267#if MagickCommandDebug >= 3 268 (void) FormatLocaleFile(stderr, "Script End: %d\n", token_info->status); 269#endif 270 switch( token_info->status ) { 271 case TokenStatusOK: 272 case TokenStatusEOF: 273 if (cli_wand->image_list_stack != (Stack *) NULL) 274 CLIWandException(OptionError,"UnbalancedParenthesis", "(eof)"); 275 else if (cli_wand->image_info_stack != (Stack *) NULL) 276 CLIWandException(OptionError,"UnbalancedBraces", "(eof)"); 277 break; 278 case TokenStatusBadQuotes: 279 /* Ensure last token has a sane length for error report */ 280 if( strlen(token_info->token) > INITAL_TOKEN_LENGTH-1 ) { 281 token_info->token[INITAL_TOKEN_LENGTH-4] = '.'; 282 token_info->token[INITAL_TOKEN_LENGTH-3] = '.'; 283 token_info->token[INITAL_TOKEN_LENGTH-2] = '.'; 284 token_info->token[INITAL_TOKEN_LENGTH-1] = '\0'; 285 } 286 CLIWandException(OptionFatalError,"ScriptUnbalancedQuotes", 287 token_info->token); 288 break; 289 case TokenStatusMemoryFailed: 290 CLIWandException(OptionFatalError,"ScriptTokenMemoryFailed",""); 291 break; 292 case TokenStatusBinary: 293 CLIWandException(OptionFatalError,"ScriptIsBinary",""); 294 break; 295 } 296 (void) fflush(stdout); 297 (void) fflush(stderr); 298 if (cli_wand->wand.debug != MagickFalse) 299 (void) LogMagickEvent(CommandEvent,GetMagickModule(), 300 "Script End \"%s\"", filename); 301 302 /* Clean up */ 303 token_info = DestroyScriptTokenInfo(token_info); 304 305 CloneString(&option,(char *) NULL); 306 CloneString(&arg1,(char *) NULL); 307 CloneString(&arg2,(char *) NULL); 308 309 return; 310} 311 312/* 313%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 314% % 315% % 316% % 317+ P r o c e s s C o m m a n d O p t i o n s % 318% % 319% % 320% % 321%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 322% 323% ProcessCommandOptions() reads and processes arguments in the given 324% command line argument array. The 'index' defines where in the array we 325% should begin processing 326% 327% The 'process_flags' can be used to control and limit option processing. 328% For example, to only process one option, or how unknown and special options 329% are to be handled, and if the last argument in array is to be regarded as a 330% final image write argument (filename or special coder). 331% 332% The format of the ProcessCommandOptions method is: 333% 334% int ProcessCommandOptions(MagickCLI *cli_wand,int argc,char **argv, 335% int index) 336% 337% A description of each parameter follows: 338% 339% o cli_wand: the main CLI Wand to use. 340% 341% o argc: the number of elements in the argument vector. 342% 343% o argv: A text array containing the command line arguments. 344% 345% o process_flags: What type of arguments will be processed, ignored 346% or return errors. 347% 348% o index: index in the argv array to start processing from 349% 350% The function returns the index ot the next option to be processed. This 351% is really only releven if process_flags contains a ProcessOneOptionOnly 352% flag. 353% 354*/ 355WandExport int ProcessCommandOptions(MagickCLI *cli_wand,int argc,char **argv, 356 int index) 357{ 358 const char 359 *option, 360 *arg1, 361 *arg2; 362 363 int 364 i, 365 end, 366 count; 367 368 CommandOptionFlags 369 option_type; 370 371 assert(argc>=index); /* you may have no arguments left! */ 372 assert(argv != (char **) NULL); 373 assert(argv[index] != (char *) NULL); 374 assert(argv[argc-1] != (char *) NULL); 375 assert(cli_wand != (MagickCLI *) NULL); 376 assert(cli_wand->signature == MagickWandSignature); 377 378 /* define the error location string for use in exceptions 379 order of localtion format escapes: filename, line, column */ 380 cli_wand->location="at %s arg %u"; 381 cli_wand->filename="CLI"; 382 cli_wand->line=index; /* note first argument we will process */ 383 384 if (cli_wand->wand.debug != MagickFalse) 385 (void) CLILogEvent(cli_wand,CommandEvent,GetMagickModule(), 386 "- Starting (\"%s\")", argv[index]); 387 388 end = argc; 389 if ( (cli_wand->process_flags & ProcessImplictWrite) != 0 ) 390 end--; /* the last arument is an implied write, do not process directly */ 391 392 for (i=index; i < end; i += count +1) { 393 /* Finished processing one option? */ 394 if ( (cli_wand->process_flags & ProcessOneOptionOnly) != 0 && i != index ) 395 return(i); 396 397 do { /* use break to loop to exception handler and loop */ 398 399 option=argv[i]; 400 cli_wand->line=i; /* note the argument for this option */ 401 402 /* get option, its argument count, and option type */ 403 cli_wand->command = GetCommandOptionInfo(argv[i]); 404 count=cli_wand->command->type; 405 option_type=(CommandOptionFlags) cli_wand->command->flags; 406#if 0 407 (void) FormatLocaleFile(stderr, "CLI %d: \"%s\" matched \"%s\"\n", 408 i, argv[i], cli_wand->command->mnemonic ); 409#endif 410 411 if ( option_type == UndefinedOptionFlag || 412 (option_type & NonMagickOptionFlag) != 0 ) { 413#if MagickCommandDebug >= 3 414 (void) FormatLocaleFile(stderr, "CLI arg %d Non-Option: \"%s\"\n", 415 i, option); 416#endif 417 if (IsCommandOption(option) == MagickFalse) { 418 if ( (cli_wand->process_flags & ProcessImplictRead) != 0 ) { 419 /* non-option -- treat as a image read */ 420 cli_wand->command=(const OptionInfo *) NULL; 421 CLIOption(cli_wand,"-read",option); 422 break; /* next option */ 423 } 424 } 425 CLIWandException(OptionFatalError,"UnrecognizedOption",option); 426 break; /* next option */ 427 } 428 429 if ( ((option_type & SpecialOptionFlag) != 0 ) && 430 ((cli_wand->process_flags & ProcessScriptOption) != 0) && 431 (LocaleCompare(option,"-script") == 0) ) { 432 /* Call Script from CLI, with a filename as a zeroth argument. 433 NOTE: -script may need to use the 'implict write filename' argument 434 so it must be handled specially to prevent a 'missing argument' error. 435 */ 436 if ( (i+count) >= argc ) 437 CLIWandException(OptionFatalError,"MissingArgument",option); 438 ProcessScriptOptions(cli_wand,argv[i+1],argc,argv,i+count); 439 return(argc); /* Script does not return to CLI -- Yet */ 440 /* FUTURE: when it does, their may be no write arg! */ 441 } 442 443 if ((i+count) >= end ) { 444 CLIWandException(OptionFatalError,"MissingArgument",option); 445 if ( CLICatchException(cli_wand, MagickFalse) != MagickFalse ) 446 return(end); 447 break; /* next option - not that their is any! */ 448 } 449 450 arg1 = ( count >= 1 ) ? argv[i+1] : (char *) NULL; 451 arg2 = ( count >= 2 ) ? argv[i+2] : (char *) NULL; 452 453 /* 454 Process Known Options 455 */ 456#if MagickCommandDebug >= 3 457 (void) FormatLocaleFile(stderr, 458 "CLI arg %u Option: \"%s\" Count: %d Flags: %04x Args: \"%s\" \"%s\"\n", 459 i,option,count,option_type,arg1,arg2); 460#endif 461 /* ignore 'genesis options' in command line args */ 462 if ( (option_type & GenesisOptionFlag) != 0 ) 463 break; /* next option */ 464 465 /* Handle any special options for CLI (-script handled above) */ 466 if ( (option_type & SpecialOptionFlag) != 0 ) { 467 if ( (cli_wand->process_flags & ProcessExitOption) != 0 468 && LocaleCompare(option,"-exit") == 0 ) 469 return(i+count); 470 break; /* next option */ 471 } 472 473 /* Process standard image option */ 474 CLIOption(cli_wand, option, arg1, arg2); 475 476DisableMSCWarning(4127) 477 } while (0); /* break block to next option */ 478RestoreMSCWarning 479 480#if MagickCommandDebug >= 5 481 (void) FormatLocaleFile(stderr, "CLI-post Image Count = %ld\n", 482 (long) GetImageListLength(cli_wand->wand.images) ); 483#endif 484 if ( CLICatchException(cli_wand, MagickFalse) != MagickFalse ) 485 return(i+count); 486 } 487 assert(i==end); 488 489 if ( (cli_wand->process_flags & ProcessImplictWrite) == 0 ) 490 return(end); /* no implied write -- just return to caller */ 491 492 assert(end==argc-1); /* end should not include last argument */ 493 494 /* 495 Implicit Write of images to final CLI argument 496 */ 497 option=argv[i]; 498 cli_wand->line=i; 499 500 /* check that stacks are empty - or cause exception */ 501 if (cli_wand->image_list_stack != (Stack *) NULL) 502 CLIWandException(OptionError,"UnbalancedParenthesis", "(end of cli)"); 503 else if (cli_wand->image_info_stack != (Stack *) NULL) 504 CLIWandException(OptionError,"UnbalancedBraces", "(end of cli)"); 505 if ( CLICatchException(cli_wand, MagickFalse) != MagickFalse ) 506 return(argc); 507 508#if MagickCommandDebug >= 3 509 (void) FormatLocaleFile(stderr,"CLI arg %d Write File: \"%s\"\n",i,option); 510#endif 511 512 /* Valid 'do no write' replacement option (instead of "null:") */ 513 if (LocaleCompare(option,"-exit") == 0 ) 514 return(argc); /* just exit, no image write */ 515 516 /* If filename looks like an option, 517 Or the common 'end of line' error of a single space. 518 -- produce an error */ 519 if (IsCommandOption(option) != MagickFalse || 520 (option[0] == ' ' && option[1] == '\0') ) { 521 CLIWandException(OptionError,"MissingOutputFilename",option); 522 return(argc); 523 } 524 525 cli_wand->command=(const OptionInfo *) NULL; 526 CLIOption(cli_wand,"-write",option); 527 return(argc); 528} 529 530/* 531%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 532% % 533% % 534% % 535+ M a g i c k I m a g e C o m m a n d % 536% % 537% % 538% % 539%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 540% 541% MagickImageCommand() Handle special use CLI arguments and prepare a 542% CLI MagickCLI to process the command line or directly specified script. 543% 544% This is essentualy interface function between the MagickCore library 545% initialization function MagickCommandGenesis(), and the option MagickCLI 546% processing functions ProcessCommandOptions() or ProcessScriptOptions() 547% 548% The format of the MagickImageCommand method is: 549% 550% MagickBooleanType MagickImageCommand(ImageInfo *image_info,int argc, 551% char **argv,char **metadata,ExceptionInfo *exception) 552% 553% A description of each parameter follows: 554% 555% o image_info: the starting image_info structure 556% (for compatibilty with MagickCommandGenisis()) 557% 558% o argc: the number of elements in the argument vector. 559% 560% o argv: A text array containing the command line arguments. 561% 562% o metadata: any metadata (for VBS) is returned here. 563% (for compatibilty with MagickCommandGenisis()) 564% 565% o exception: return any errors or warnings in this structure. 566% 567*/ 568 569static void MagickUsage(MagickBooleanType verbose) 570{ 571 const char 572 *name; 573 574 size_t 575 len; 576 577 name=GetClientName(); 578 len=strlen(name); 579 580 if (len>=7 && LocaleCompare("convert",name+len-7) == 0) { 581 /* convert usage */ 582 (void) FormatLocaleFile(stdout, 583 "Usage: %s [ {option} | {image} ... ] {output_image}\n",name); 584 (void) FormatLocaleFile(stdout, 585 " %s -help | -version | -usage | -list {option}\n\n",name); 586 return; 587 } 588 else if (len>=6 && LocaleCompare("script",name+len-6) == 0) { 589 /* magick-script usage */ 590 (void) FormatLocaleFile(stdout, 591 "Usage: %s {filename} [ {script_args} ... ]\n",name); 592 } 593 else { 594 /* magick usage */ 595 (void) FormatLocaleFile(stdout, 596 "Usage: %s [ {option} | {image} ... ] {output_image}\n",name); 597 (void) FormatLocaleFile(stdout, 598 " %s [ {option} | {image} ... ] -script {filename} [ {script_args} ...]\n", 599 name); 600 } 601 (void) FormatLocaleFile(stdout, 602 " %s -help | -version | -usage | -list {option}\n\n",name); 603 604 if (verbose == MagickFalse) 605 return; 606 607 (void) FormatLocaleFile(stdout,"%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", 608 "All options are performed in a strict 'as you see them' order\n", 609 "You must read-in images before you can operate on them.\n", 610 "\n", 611 "Magick Script files can use any of the following forms...\n", 612 " #!/path/to/magick -script\n", 613 "or\n", 614 " #!/bin/sh\n", 615 " :; exec magick -script \"$0\" \"$@\"; exit 10\n", 616 " # Magick script from here...\n", 617 "or\n", 618 " #!/usr/bin/env magick-script\n", 619 "The latter two forms do not require the path to the command hard coded.\n", 620 "Note: \"magick-script\" needs to be linked to the \"magick\" command.\n", 621 "\n", 622 "For more information on usage, options, examples, and techniques\n", 623 "see the ImageMagick website at ", MagickAuthoritativeURL); 624 625 return; 626} 627 628/* 629 Concatanate given file arguments to the given output argument. 630 Used for a special -concatenate option used for specific 'delegates'. 631 The option is not formally documented. 632 633 magick -concatenate files... output 634 635 This is much like the UNIX "cat" command, but for both UNIX and Windows, 636 however the last argument provides the output filename. 637*/ 638static MagickBooleanType ConcatenateImages(int argc,char **argv, 639 ExceptionInfo *exception ) 640{ 641 FILE 642 *input, 643 *output; 644 645 MagickBooleanType 646 status; 647 648 int 649 c; 650 651 register ssize_t 652 i; 653 654 if (ExpandFilenames(&argc,&argv) == MagickFalse) 655 ThrowFileException(exception,ResourceLimitError,"MemoryAllocationFailed", 656 GetExceptionMessage(errno)); 657 output=fopen_utf8(argv[argc-1],"wb"); 658 if (output == (FILE *) NULL) 659 { 660 ThrowFileException(exception,FileOpenError,"UnableToOpenFile", 661 argv[argc-1]); 662 return(MagickFalse); 663 } 664 status=MagickTrue; 665 for (i=2; i < (ssize_t) (argc-1); i++) 666 { 667 input=fopen_utf8(argv[i],"rb"); 668 if (input == (FILE *) NULL) 669 { 670 ThrowFileException(exception,FileOpenError,"UnableToOpenFile",argv[i]); 671 continue; 672 } 673 for (c=fgetc(input); c != EOF; c=fgetc(input)) 674 if (fputc((char) c,output) != c) 675 status=MagickFalse; 676 (void) fclose(input); 677 (void) remove_utf8(argv[i]); 678 } 679 (void) fclose(output); 680 return(status); 681} 682 683WandExport MagickBooleanType MagickImageCommand(ImageInfo *image_info,int argc, 684 char **argv,char **metadata,ExceptionInfo *exception) 685{ 686 MagickCLI 687 *cli_wand; 688 689 size_t 690 len; 691 692 assert(image_info != (ImageInfo *) NULL); 693 694 /* For specific OS command line requirements */ 695 ReadCommandlLine(argc,&argv); 696 697 /* Initialize special "CLI Wand" to hold images and settings (empty) */ 698 cli_wand=AcquireMagickCLI(image_info,exception); 699 cli_wand->location="Initializing"; 700 cli_wand->filename=argv[0]; 701 cli_wand->line=1; 702 703 if (cli_wand->wand.debug != MagickFalse) 704 (void) CLILogEvent(cli_wand,CommandEvent,GetMagickModule(), 705 "\"%s\"",argv[0]); 706 707 708 GetPathComponent(argv[0],TailPath,cli_wand->wand.name); 709 SetClientName(cli_wand->wand.name); 710 ConcatenateMagickString(cli_wand->wand.name,"-CLI",MagickPathExtent); 711 712 len=strlen(argv[0]); /* precaution */ 713 714 /* "convert" command - give a "deprecated" warning" */ 715 if (len>=7 && LocaleCompare("convert",argv[0]+len-7) == 0) { 716 cli_wand->process_flags = ConvertCommandOptionFlags; 717 (void) FormatLocaleFile(stderr,"WARNING: %s\n", 718 "The convert command is deprecated in IMv7, use \"magick\"\n"); 719 } 720 721 /* Special Case: If command name ends with "script" implied "-script" */ 722 if (len>=6 && LocaleCompare("script",argv[0]+len-6) == 0) { 723 if (argc >= 2 && ( (*(argv[1]) != '-') || (strlen(argv[1]) == 1) )) { 724 GetPathComponent(argv[1],TailPath,cli_wand->wand.name); 725 ProcessScriptOptions(cli_wand,argv[1],argc,argv,2); 726 goto Magick_Command_Cleanup; 727 } 728 } 729 730 /* Special Case: Version Information and Abort */ 731 if (argc == 2) { 732 if ((LocaleCompare("-version",argv[1]) == 0) || /* GNU standard option */ 733 (LocaleCompare("--version",argv[1]) == 0) ) { /* just version */ 734 CLIOption(cli_wand, "-version"); 735 goto Magick_Command_Exit; 736 } 737 if ((LocaleCompare("-help",argv[1]) == 0) || /* GNU standard option */ 738 (LocaleCompare("--help",argv[1]) == 0) ) { /* just a brief summary */ 739 if (cli_wand->wand.debug != MagickFalse) 740 (void) CLILogEvent(cli_wand,CommandEvent,GetMagickModule(), 741 "- Special Option \"%s\"", argv[1]); 742 MagickUsage(MagickFalse); 743 goto Magick_Command_Exit; 744 } 745 if (LocaleCompare("-usage",argv[1]) == 0) { /* both version & usage */ 746 if (cli_wand->wand.debug != MagickFalse) 747 (void) CLILogEvent(cli_wand,CommandEvent,GetMagickModule(), 748 "- Special Option \"%s\"", argv[1]); 749 CLIOption(cli_wand, "-version" ); 750 MagickUsage(MagickTrue); 751 goto Magick_Command_Exit; 752 } 753 } 754 755 /* not enough arguments -- including -help */ 756 if (argc < 3) { 757 (void) FormatLocaleFile(stderr, 758 "Error: Invalid argument or not enough arguments\n\n"); 759 MagickUsage(MagickFalse); 760 goto Magick_Command_Exit; 761 } 762 763 /* Special "concatenate option (hidden) for delegate usage */ 764 if (LocaleCompare("-concatenate",argv[1]) == 0) { 765 if (cli_wand->wand.debug != MagickFalse) 766 (void) CLILogEvent(cli_wand,CommandEvent,GetMagickModule(), 767 "- Special Option \"%s\"", argv[1]); 768 ConcatenateImages(argc,argv,exception); 769 goto Magick_Command_Exit; 770 } 771 772 /* List Information and Abort */ 773 if (argc == 3 && LocaleCompare("-list",argv[1]) == 0) { 774 CLIOption(cli_wand, argv[1], argv[2]); 775 goto Magick_Command_Exit; 776 } 777 778 /* ------------- */ 779 /* The Main Call */ 780 781 if (LocaleCompare("-script",argv[1]) == 0) { 782 /* Start processing directly from script, no pre-script options 783 Replace wand command name with script name 784 First argument in the argv array is the script name to read. 785 */ 786 GetPathComponent(argv[2],TailPath,cli_wand->wand.name); 787 ProcessScriptOptions(cli_wand,argv[2],argc,argv,3); 788 } 789 else { 790 /* Normal Command Line, assumes output file as last option */ 791 ProcessCommandOptions(cli_wand,argc,argv,1); 792 } 793 /* ------------- */ 794 795Magick_Command_Cleanup: 796 cli_wand->location="Cleanup"; 797 cli_wand->filename=argv[0]; 798 if (cli_wand->wand.debug != MagickFalse) 799 (void) CLILogEvent(cli_wand,CommandEvent,GetMagickModule(), 800 "\"%s\"",argv[0]); 801 802 /* recover original image_info and clean up stacks 803 FUTURE: "-reset stacks" option */ 804 while ((cli_wand->image_list_stack != (Stack *) NULL) && 805 (cli_wand->image_list_stack->next != (Stack *) NULL)) 806 CLIOption(cli_wand,")"); 807 while ((cli_wand->image_info_stack != (Stack *) NULL) && 808 (cli_wand->image_info_stack->next != (Stack *) NULL)) 809 CLIOption(cli_wand,"}"); 810 811 /* assert we have recovered the original structures */ 812 assert(cli_wand->wand.image_info == image_info); 813 assert(cli_wand->wand.exception == exception); 814 815 /* Handle metadata for ImageMagickObject COM object for Windows VBS */ 816 if (metadata != (char **) NULL) { 817 const char 818 *format; 819 820 char 821 *text; 822 823 format="%w,%h,%m"; // Get this from image_info Option splaytree 824 825 text=InterpretImageProperties(image_info,cli_wand->wand.images,format, 826 exception); 827 if (text == (char *) NULL) 828 ThrowMagickException(exception,GetMagickModule(),ResourceLimitError, 829 "MemoryAllocationFailed","`%s'", GetExceptionMessage(errno)); 830 else { 831 (void) ConcatenateString(&(*metadata),text); 832 text=DestroyString(text); 833 } 834 } 835 836Magick_Command_Exit: 837 cli_wand->location="Exiting"; 838 cli_wand->filename=argv[0]; 839 if (cli_wand->wand.debug != MagickFalse) 840 (void) CLILogEvent(cli_wand,CommandEvent,GetMagickModule(), 841 "\"%s\"",argv[0]); 842 843 /* Destroy the special CLI Wand */ 844 cli_wand->wand.image_info = (ImageInfo *) NULL; /* not these */ 845 cli_wand->wand.exception = (ExceptionInfo *) NULL; 846 cli_wand=DestroyMagickCLI(cli_wand); 847 848 return(exception->severity < ErrorException ? MagickTrue : MagickFalse); 849} 850