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