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