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