MagickWand 7.1.2-26
Convert, Edit, Or Compose Bitmap Images
Loading...
Searching...
No Matches
magick-cli.c
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 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% https://imagemagick.org/license/ %
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/policy-private.h"
58#include "MagickCore/string-private.h"
59#include "MagickCore/thread-private.h"
60#include "MagickCore/utility-private.h"
61#include "MagickCore/exception-private.h"
62#include "MagickCore/version.h"
63
64/* verbose debugging,
65 0 - no debug lines
66 3 - show option details (better to use -debug Command now)
67 5 - image counts (after option runs)
68*/
69#define MagickCommandDebug 0
70
71/*
72%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
73% %
74% %
75% %
76% M a g i c k C o m m a n d G e n e s i s %
77% %
78% %
79% %
80%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
81%
82% MagickCommandGenesis() applies image processing options to an image as
83% prescribed by command line options.
84%
85% It wiil look for special options like "-debug", "-bench", and
86% "-distribute-cache" that needs to be applied even before the main
87% processing begins, and may completely overrule normal command processing.
88% Such 'Genesis' Options can only be given on the CLI, (not in a script)
89% and are typically ignored (as they have been handled) if seen later.
90%
91% The format of the MagickCommandGenesis method is:
92%
93% MagickBooleanType MagickCommandGenesis(ImageInfo *image_info,
94% MagickCommand command,int argc,char **argv,char **metadata,
95% ExceptionInfo *exception)
96%
97% A description of each parameter follows:
98%
99% o image_info: the image info.
100%
101% o command: Choose from ConvertImageCommand, IdentifyImageCommand,
102% MogrifyImageCommand, CompositeImageCommand, CompareImagesCommand,
103% ConjureImageCommand, StreamImageCommand, ImportImageCommand,
104% DisplayImageCommand, or AnimateImageCommand.
105%
106% o argc: Specifies a pointer to an integer describing the number of
107% elements in the argument vector.
108%
109% o argv: Specifies a pointer to a text array containing the command line
110% arguments.
111%
112% o metadata: any metadata is returned here.
113%
114% o exception: return any errors or warnings in this structure.
115%
116*/
117WandExport MagickBooleanType MagickCommandGenesis(ImageInfo *image_info,
118 MagickCommand command,int argc,char **argv,char **metadata,
119 ExceptionInfo *exception)
120{
121 char
122 client_name[MagickPathExtent],
123 *option;
124
125 double
126 duration,
127 serial;
128
129 MagickBooleanType
130 concurrent,
131 regard_warnings,
132 status;
133
134 size_t
135 iterations,
136 number_threads;
137
138 ssize_t
139 i,
140 n;
141
142 (void) setlocale(LC_ALL,"");
143 (void) setlocale(LC_NUMERIC,"C");
144 GetPathComponent(argv[0],TailPath,client_name);
145 (void) SetClientName(client_name);
146 concurrent=MagickFalse;
147 duration=(-1.0);
148 iterations=1;
149 status=MagickTrue;
150 regard_warnings=MagickFalse;
151 for (i=1; i < ((ssize_t) argc-1); i++)
152 {
153 option=argv[i];
154 if ((strlen(option) == 1) || ((*option != '-') && (*option != '+')))
155 continue;
156 if (LocaleCompare("-bench",option) == 0)
157 iterations=StringToUnsignedLong(argv[++i]);
158 if (LocaleCompare("-concurrent",option) == 0)
159 concurrent=MagickTrue;
160 if (LocaleCompare("-debug",option) == 0)
161 (void) SetLogEventMask(argv[++i]);
162 if (LocaleCompare("-distribute-cache",option) == 0)
163 {
164 DistributePixelCacheServer(StringToInteger(argv[++i]),exception);
165 exit(0);
166 }
167 if (LocaleCompare("-duration",option) == 0)
168 duration=StringToDouble(argv[++i],(char **) NULL);
169 if (LocaleCompare("-regard-warnings",option) == 0)
170 regard_warnings=MagickTrue;
171 }
172 if (iterations == 1)
173 {
174 char
175 *text;
176
177 text=(char *) NULL;
178 status=command(image_info,argc,argv,&text,exception);
179 if (exception->severity != UndefinedException)
180 {
181 if ((exception->severity > ErrorException) ||
182 (regard_warnings != MagickFalse))
183 status=MagickFalse;
184 CatchException(exception);
185 }
186 if (text != (char *) NULL)
187 {
188 if (metadata != (char **) NULL)
189 (void) ConcatenateString(&(*metadata),text);
190 text=DestroyString(text);
191 }
192 return(status);
193 }
194 number_threads=GetOpenMPMaximumThreads();
195 serial=0.0;
196 for (n=1; n <= (ssize_t) number_threads; n++)
197 {
198 double
199 e,
200 parallel,
201 user_time;
202
203 TimerInfo
204 *timer;
205
206 (void) SetMagickResourceLimit(ThreadResource,(MagickSizeType) n);
207 timer=AcquireTimerInfo();
208 if (concurrent == MagickFalse)
209 {
210 for (i=0; i < (ssize_t) iterations; i++)
211 {
212 char
213 *text;
214
215 text=(char *) NULL;
216 if (status == MagickFalse)
217 continue;
218 if (duration > 0)
219 {
220 if (GetElapsedTime(timer) > duration)
221 continue;
222 (void) ContinueTimer(timer);
223 }
224 status=command(image_info,argc,argv,&text,exception);
225 if (exception->severity != UndefinedException)
226 {
227 if ((exception->severity > ErrorException) ||
228 (regard_warnings != MagickFalse))
229 status=MagickFalse;
230 CatchException(exception);
231 }
232 if (text != (char *) NULL)
233 {
234 if (metadata != (char **) NULL)
235 (void) ConcatenateString(&(*metadata),text);
236 text=DestroyString(text);
237 }
238 }
239 }
240 else
241 {
242 SetOpenMPMaxActiveLevels(1);
243#if defined(MAGICKCORE_OPENMP_SUPPORT)
244 # pragma omp parallel for shared(status)
245#endif
246 for (i=0; i < (ssize_t) iterations; i++)
247 {
248 char
249 *text;
250
251 text=(char *) NULL;
252 if (status == MagickFalse)
253 continue;
254 if (duration > 0)
255 {
256 if (GetElapsedTime(timer) > duration)
257 continue;
258 (void) ContinueTimer(timer);
259 }
260 status=command(image_info,argc,argv,&text,exception);
261#if defined(MAGICKCORE_OPENMP_SUPPORT)
262 # pragma omp critical (MagickCore_MagickCommandGenesis)
263#endif
264 {
265 if (exception->severity != UndefinedException)
266 {
267 if ((exception->severity > ErrorException) ||
268 (regard_warnings != MagickFalse))
269 status=MagickFalse;
270 CatchException(exception);
271 }
272 if (text != (char *) NULL)
273 {
274 if (metadata != (char **) NULL)
275 (void) ConcatenateString(&(*metadata),text);
276 text=DestroyString(text);
277 }
278 }
279 }
280 }
281 user_time=GetUserTime(timer);
282 parallel=GetElapsedTime(timer);
283 e=1.0;
284 if (n == 1)
285 serial=parallel;
286 else
287 e=((1.0/(1.0/((serial/(serial+parallel))+(1.0-(serial/(serial+parallel)))/
288 (double) n)))-(1.0/(double) n))/(1.0-1.0/(double) n);
289 (void) FormatLocaleFile(stderr,
290 " Performance[%.20g]: %.20gi %0.3fips %0.6fe %0.6fu %lu:%02lu.%03lu\n",
291 (double) n,(double) iterations,(double) iterations/parallel,e,user_time,
292 (unsigned long) (parallel/60.0),(unsigned long) floor(fmod(parallel,
293 60.0)),(unsigned long) (1000.0*(parallel-floor(parallel))+0.5));
294 timer=DestroyTimerInfo(timer);
295 }
296 return(status);
297}
298
299/*
300%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
301% %
302% %
303% %
304+ P r o c e s s S c r i p t O p t i o n s %
305% %
306% %
307% %
308%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
309%
310% ProcessScriptOptions() reads options and processes options as they are
311% found in the given file, or pipeline. The filename to open and read
312% options is given as the 'index' argument of the argument array given.
313%
314% Other arguments following index may be read by special script options
315% as settings (strings), images, or as operations to be processed in various
316% ways. How they are treated is up to the script being processed.
317%
318% Note that a script not 'return' to the command line processing, nor can
319% they call (and return from) other scripts. At least not at this time.
320%
321% There are no 'ProcessOptionFlags' control flags at this time.
322%
323% The format of the ProcessScriptOptions method is:
324%
325% void ProcessScriptOptions(MagickCLI *cli_wand,const char *filename,
326% int argc,char **argv,int index)
327%
328% A description of each parameter follows:
329%
330% o cli_wand: the main CLI Wand to use.
331%
332% o filename: the filename of script to process
333%
334% o argc: the number of elements in the argument vector. (optional)
335%
336% o argv: A text array containing the command line arguments. (optional)
337%
338% o index: offset of next argument in argv (script arguments) (optional)
339%
340*/
341WandExport void ProcessScriptOptions(MagickCLI *cli_wand,const char *filename,
342 int magick_unused(argc),char **magick_unused(argv),int magick_unused(index))
343{
345 *token_info;
346
347 CommandOptionFlags
348 option_type;
349
350 int
351 count;
352
353 char
354 *option,
355 *arg1,
356 *arg2;
357
358 magick_unreferenced(argc);
359 magick_unreferenced(argv);
360 magick_unreferenced(index);
361 assert(filename != (char *) NULL ); /* at least one argument - script name */
362 assert(cli_wand != (MagickCLI *) NULL);
363 assert(cli_wand->signature == MagickWandSignature);
364 if (cli_wand->wand.debug != MagickFalse)
365 (void) LogMagickEvent(CommandEvent,GetMagickModule(),
366 "Processing script \"%s\"", filename);
367
368 /* open file script or stream, and set up tokenizer */
369 token_info = AcquireScriptTokenInfo(filename);
370 if (token_info == (ScriptTokenInfo *) NULL) {
371 CLIWandExceptionFile(OptionFatalError,"UnableToOpenScript",filename);
372 return;
373 }
374
375 /* define the error location string for use in exceptions
376 order of location format escapes: filename, line, column */
377 cli_wand->location="in \"%s\" at line %u,column %u";
378 if ( LocaleCompare("-", filename) == 0 )
379 cli_wand->filename="stdin";
380 else
381 cli_wand->filename=filename;
382
383 /* Process Options from Script */
384 option = arg1 = arg2 = (char*) NULL;
385DisableMSCWarning(4127)
386 while (1) {
387RestoreMSCWarning
388
389 { MagickBooleanType status = GetScriptToken(token_info);
390 cli_wand->line=token_info->token_line;
391 cli_wand->column=token_info->token_column;
392 if (status == MagickFalse)
393 break; /* error or end of options */
394 }
395
396 do { /* use break to loop to exception handler and loop */
397
398 /* save option details */
399 (void) CloneString(&option,token_info->token);
400
401 /* get option, its argument count, and option type */
402 cli_wand->command = GetCommandOptionInfo(option);
403 count=(int) cli_wand->command->type;
404 option_type=(CommandOptionFlags) cli_wand->command->flags;
405#if 0
406 (void) FormatLocaleFile(stderr, "Script: %u,%u: \"%s\" matched \"%s\"\n",
407 cli_wand->line, cli_wand->line, option, cli_wand->command->mnemonic );
408#endif
409
410 /* handle a undefined option - image read - always for "magick-script" */
411 if ( option_type == UndefinedOptionFlag ||
412 (option_type & NonMagickOptionFlag) != 0 ) {
413#if MagickCommandDebug >= 3
414 (void) FormatLocaleFile(stderr, "Script %u,%u Non-Option: \"%s\"\n",
415 cli_wand->line, cli_wand->line, option);
416#endif
417 if (IsCommandOption(option) == MagickFalse) {
418 /* non-option -- treat as a image read */
419 cli_wand->command=(const OptionInfo *) NULL;
420 CLIOption(cli_wand,"-read",option);
421 break; /* next option */
422 }
423 CLIWandException(OptionFatalError,"UnrecognizedOption",option);
424 break; /* next option */
425 }
426
427 if ( count >= 1 ) {
428 if (GetScriptToken(token_info) == MagickFalse)
429 CLIWandException(OptionFatalError,"MissingArgument",option);
430 (void) CloneString(&arg1,token_info->token);
431 }
432 else
433 (void) CloneString(&arg1,(char *) NULL);
434
435 if ( count >= 2 ) {
436 if (GetScriptToken(token_info) == MagickFalse)
437 CLIWandExceptionBreak(OptionFatalError,"MissingArgument",option);
438 (void) CloneString(&arg2,token_info->token);
439 }
440 else
441 (void) CloneString(&arg2,(char *) NULL);
442
443 /*
444 Process Options
445 */
446#if MagickCommandDebug >= 3
447 (void) FormatLocaleFile(stderr,
448 "Script %u,%u Option: \"%s\" Count: %d Flags: %04x Args: \"%s\" \"%s\"\n",
449 cli_wand->line,cli_wand->line,option,count,option_type,arg1,arg2);
450#endif
451 /* Hard Deprecated Options, no code to execute - error */
452 if ( (option_type & DeprecateOptionFlag) != 0 ) {
453 CLIWandException(OptionError,"DeprecatedOptionNoCode",option);
454 break; /* next option */
455 }
456
457 /* MagickCommandGenesis() options have no place in a magick script */
458 if ( (option_type & GenesisOptionFlag) != 0 ) {
459 CLIWandException(OptionError,"InvalidUseOfOption",option);
460 break; /* next option */
461 }
462
463 /* handle any special 'script' options */
464 if ( (option_type & SpecialOptionFlag) != 0 ) {
465 if ( LocaleCompare(option,"-exit") == 0 ) {
466 goto loop_exit; /* break out of loop - return from script */
467 }
468 if ( LocaleCompare(option,"-script") == 0 ) {
469 /* FUTURE: call new script from this script - error for now */
470 CLIWandException(OptionError,"InvalidUseOfOption",option);
471 break; /* next option */
472 }
473 /* FUTURE: handle special script-argument options here */
474 /* handle any other special operators now */
475 CLIWandException(OptionError,"InvalidUseOfOption",option);
476 break; /* next option */
477 }
478
479 /* Process non-specific Option */
480 CLIOption(cli_wand, option, arg1, arg2);
481 (void) fflush(stdout);
482 (void) fflush(stderr);
483
484DisableMSCWarning(4127)
485 } while (0); /* break block to next option */
486RestoreMSCWarning
487
488#if MagickCommandDebug >= 5
489 fprintf(stderr, "Script Image Count = %ld\n",
490 GetImageListLength(cli_wand->wand.images) );
491#endif
492 if (CLICatchException(cli_wand, MagickFalse) != MagickFalse)
493 break; /* exit loop */
494 }
495
496 /*
497 Loop exit - check for some tokenization error
498 */
499loop_exit:
500#if MagickCommandDebug >= 3
501 (void) FormatLocaleFile(stderr, "Script End: %d\n", token_info->status);
502#endif
503 switch( token_info->status ) {
504 case TokenStatusOK:
505 case TokenStatusEOF:
506 if (cli_wand->image_list_stack != (CLIStack *) NULL)
507 CLIWandException(OptionError,"UnbalancedParenthesis", "(eof)");
508 else if (cli_wand->image_info_stack != (CLIStack *) NULL)
509 CLIWandException(OptionError,"UnbalancedBraces", "(eof)");
510 break;
511 case TokenStatusBadQuotes:
512 /* Ensure last token has a sane length for error report */
513 if( strlen(token_info->token) > INITAL_TOKEN_LENGTH-1 ) {
514 token_info->token[INITAL_TOKEN_LENGTH-4] = '.';
515 token_info->token[INITAL_TOKEN_LENGTH-3] = '.';
516 token_info->token[INITAL_TOKEN_LENGTH-2] = '.';
517 token_info->token[INITAL_TOKEN_LENGTH-1] = '\0';
518 }
519 CLIWandException(OptionFatalError,"ScriptUnbalancedQuotes",
520 token_info->token);
521 break;
522 case TokenStatusMemoryFailed:
523 CLIWandException(OptionFatalError,"ScriptTokenMemoryFailed","");
524 break;
525 case TokenStatusBinary:
526 CLIWandException(OptionFatalError,"ScriptIsBinary","");
527 break;
528 }
529 (void) fflush(stdout);
530 (void) fflush(stderr);
531 if (cli_wand->wand.debug != MagickFalse)
532 (void) LogMagickEvent(CommandEvent,GetMagickModule(),
533 "Script End \"%s\"", filename);
534
535 /* Clean up */
536 token_info = DestroyScriptTokenInfo(token_info);
537
538 (void) CloneString(&option,(char *) NULL);
539 (void) CloneString(&arg1,(char *) NULL);
540 (void) CloneString(&arg2,(char *) NULL);
541
542 return;
543}
544
545/*
546%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
547% %
548% %
549% %
550+ P r o c e s s C o m m a n d O p t i o n s %
551% %
552% %
553% %
554%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
555%
556% ProcessCommandOptions() reads and processes arguments in the given
557% command line argument array. The 'index' defines where in the array we
558% should begin processing
559%
560% The 'process_flags' can be used to control and limit option processing.
561% For example, to only process one option, or how unknown and special options
562% are to be handled, and if the last argument in array is to be regarded as a
563% final image write argument (filename or special coder).
564%
565% The format of the ProcessCommandOptions method is:
566%
567% int ProcessCommandOptions(MagickCLI *cli_wand,int argc,char **argv,
568% int index)
569%
570% A description of each parameter follows:
571%
572% o cli_wand: the main CLI Wand to use.
573%
574% o argc: the number of elements in the argument vector.
575%
576% o argv: A text array containing the command line arguments.
577%
578% o process_flags: What type of arguments will be processed, ignored
579% or return errors.
580%
581% o index: index in the argv array to start processing from
582%
583% The function returns the index ot the next option to be processed. This
584% is really only relevant if process_flags contains a ProcessOneOptionOnly
585% flag.
586%
587*/
588WandExport int ProcessCommandOptions(MagickCLI *cli_wand,int argc,char **argv,
589 int index)
590{
591 const char
592 *option,
593 *arg1,
594 *arg2;
595
596 int
597 i,
598 end,
599 count;
600
601 CommandOptionFlags
602 option_type;
603
604 assert(argc>=index); /* you may have no arguments left! */
605 assert(argv != (char **) NULL);
606 assert(argv[index] != (char *) NULL);
607 assert(argv[argc-1] != (char *) NULL);
608 assert(cli_wand != (MagickCLI *) NULL);
609 assert(cli_wand->signature == MagickWandSignature);
610
611 /* define the error location string for use in exceptions
612 order of location format escapes: filename, line, column */
613 cli_wand->location="at %s arg %u";
614 cli_wand->filename="CLI";
615 cli_wand->line=(size_t) index; /* note first argument we will process */
616
617 if (cli_wand->wand.debug != MagickFalse)
618 (void) CLILogEvent(cli_wand,CommandEvent,GetMagickModule(),
619 "- Starting (\"%s\")", argv[index]);
620
621 end = argc;
622 if ( (cli_wand->process_flags & ProcessImplicitWrite) != 0 )
623 end--; /* the last argument is an implied write, do not process directly */
624
625 for (i=index; i < end; i += count +1) {
626 /* Finished processing one option? */
627 if ( (cli_wand->process_flags & ProcessOneOptionOnly) != 0 && i != index )
628 return(i);
629
630 do { /* use break to loop to exception handler and loop */
631
632 option=argv[i];
633 cli_wand->line=(size_t) i; /* note the argument for this option */
634
635 /* get option, its argument count, and option type */
636 cli_wand->command = GetCommandOptionInfo(argv[i]);
637 count=(int) cli_wand->command->type;
638 option_type=(CommandOptionFlags) cli_wand->command->flags;
639#if 0
640 (void) FormatLocaleFile(stderr, "CLI %d: \"%s\" matched \"%s\"\n",
641 i, argv[i], cli_wand->command->mnemonic );
642#endif
643
644 if ( option_type == UndefinedOptionFlag ||
645 (option_type & NonMagickOptionFlag) != 0 ) {
646#if MagickCommandDebug >= 3
647 (void) FormatLocaleFile(stderr, "CLI arg %d Non-Option: \"%s\"\n",
648 i, option);
649#endif
650 if (IsCommandOption(option) == MagickFalse) {
651 if ( (cli_wand->process_flags & ProcessImplicitRead) != 0 ) {
652 /* non-option -- treat as a image read */
653 cli_wand->command=(const OptionInfo *) NULL;
654 CLIOption(cli_wand,"-read",option);
655 break; /* next option */
656 }
657 }
658 CLIWandException(OptionFatalError,"UnrecognizedOption",option);
659 break; /* next option */
660 }
661
662 if ( ((option_type & SpecialOptionFlag) != 0 ) &&
663 ((cli_wand->process_flags & ProcessScriptOption) != 0) &&
664 (LocaleCompare(option,"-script") == 0) ) {
665 /* Call Script from CLI, with a filename as a zeroth argument.
666 NOTE: -script may need to use the 'implicit write filename' argument
667 so it must be handled specially to prevent a 'missing argument' error.
668 */
669 if ( (i+count) >= argc )
670 CLIWandException(OptionFatalError,"MissingArgument",option);
671 ProcessScriptOptions(cli_wand,argv[i+1],argc,argv,i+count);
672 return(argc); /* Script does not return to CLI -- Yet */
673 /* FUTURE: when it does, their may be no write arg! */
674 }
675
676 if ((i+count) >= end ) {
677 CLIWandException(OptionFatalError,"MissingArgument",option);
678 if ( CLICatchException(cli_wand, MagickFalse) != MagickFalse )
679 return(end);
680 break; /* next option - not that their is any! */
681 }
682
683 arg1 = ( count >= 1 ) ? argv[i+1] : (char *) NULL;
684 arg2 = ( count >= 2 ) ? argv[i+2] : (char *) NULL;
685
686 /*
687 Process Known Options
688 */
689#if MagickCommandDebug >= 3
690 (void) FormatLocaleFile(stderr,
691 "CLI arg %u Option: \"%s\" Count: %d Flags: %04x Args: \"%s\" \"%s\"\n",
692 i,option,count,option_type,arg1,arg2);
693#endif
694 /* ignore 'genesis options' in command line args */
695 if ( (option_type & GenesisOptionFlag) != 0 )
696 break; /* next option */
697
698 /* Handle any special options for CLI (-script handled above) */
699 if ( (option_type & SpecialOptionFlag) != 0 ) {
700 if ( (cli_wand->process_flags & ProcessExitOption) != 0
701 && LocaleCompare(option,"-exit") == 0 )
702 return(i+count);
703 break; /* next option */
704 }
705
706 /* Process standard image option */
707 CLIOption(cli_wand, option, arg1, arg2);
708
709DisableMSCWarning(4127)
710 } while (0); /* break block to next option */
711RestoreMSCWarning
712
713#if MagickCommandDebug >= 5
714 (void) FormatLocaleFile(stderr, "CLI-post Image Count = %ld\n",
715 (long) GetImageListLength(cli_wand->wand.images) );
716#endif
717 if ( CLICatchException(cli_wand, MagickFalse) != MagickFalse )
718 return(i+count);
719 }
720 assert(i==end);
721
722 if ( (cli_wand->process_flags & ProcessImplicitWrite) == 0 )
723 return(end); /* no implied write -- just return to caller */
724
725 assert(end==argc-1); /* end should not include last argument */
726
727 /*
728 Implicit Write of images to final CLI argument
729 */
730 option=argv[i];
731 cli_wand->line=(size_t) i;
732
733 /* check that stacks are empty - or cause exception */
734 if (cli_wand->image_list_stack != (CLIStack *) NULL)
735 CLIWandException(OptionError,"UnbalancedParenthesis", "(end of cli)");
736 else if (cli_wand->image_info_stack != (CLIStack *) NULL)
737 CLIWandException(OptionError,"UnbalancedBraces", "(end of cli)");
738 if ( CLICatchException(cli_wand, MagickFalse) != MagickFalse )
739 return(argc);
740
741#if MagickCommandDebug >= 3
742 (void) FormatLocaleFile(stderr,"CLI arg %d Write File: \"%s\"\n",i,option);
743#endif
744
745 /* Valid 'do no write' replacement option (instead of "null:") */
746 if (LocaleCompare(option,"-exit") == 0 )
747 return(argc); /* just exit, no image write */
748
749 /* If filename looks like an option,
750 Or the common 'end of line' error of a single space.
751 -- produce an error */
752 if (IsCommandOption(option) != MagickFalse ||
753 (option[0] == ' ' && option[1] == '\0') ) {
754 CLIWandException(OptionError,"MissingOutputFilename",option);
755 return(argc);
756 }
757
758 cli_wand->command=(const OptionInfo *) NULL;
759 CLIOption(cli_wand,"-write",option);
760 return(argc);
761}
762
763/*
764%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
765% %
766% %
767% %
768+ M a g i c k I m a g e C o m m a n d %
769% %
770% %
771% %
772%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
773%
774% MagickImageCommand() Handle special use CLI arguments and prepare a
775% CLI MagickCLI to process the command line or directly specified script.
776%
777% This is essentially interface function between the MagickCore library
778% initialization function MagickCommandGenesis(), and the option MagickCLI
779% processing functions ProcessCommandOptions() or ProcessScriptOptions()
780%
781% The format of the MagickImageCommand method is:
782%
783% MagickBooleanType MagickImageCommand(ImageInfo *image_info,int argc,
784% char **argv,char **metadata,ExceptionInfo *exception)
785%
786% A description of each parameter follows:
787%
788% o image_info: the starting image_info structure
789% (for compatibility with MagickCommandGenisis())
790%
791% o argc: the number of elements in the argument vector.
792%
793% o argv: A text array containing the command line arguments.
794%
795% o metadata: any metadata (for VBS) is returned here.
796% (for compatibility with MagickCommandGenisis())
797%
798% o exception: return any errors or warnings in this structure.
799%
800*/
801
802static MagickBooleanType MagickCommandUsage(void)
803{
804 static const char
805 channel_operators[] =
806 " -channel-fx expression\n"
807 " exchange, extract, or transfer one or more image channels\n"
808 " -separate separate an image channel into a grayscale image",
809 miscellaneous[] =
810 " -debug events display copious debugging information\n"
811 " -distribute-cache port\n"
812 " distributed pixel cache spanning one or more servers\n"
813 " -help print program options\n"
814 " -list type print a list of supported option arguments\n"
815 " -log format format of debugging information\n"
816 " -usage print program usage\n"
817 " -version print version information",
818 operators[] =
819 " -adaptive-blur geometry\n"
820 " adaptively blur pixels; decrease effect near edges\n"
821 " -adaptive-resize geometry\n"
822 " adaptively resize image using 'mesh' interpolation\n"
823 " -adaptive-sharpen geometry\n"
824 " adaptively sharpen pixels; increase effect near edges\n"
825 " -alpha option on, activate, off, deactivate, set, opaque, copy\n"
826 " transparent, extract, background, or shape\n"
827 " -annotate geometry text\n"
828 " annotate the image with text\n"
829 " -auto-gamma automagically adjust gamma level of image\n"
830 " -auto-level automagically adjust color levels of image\n"
831 " -auto-orient automagically orient (rotate) image\n"
832 " -auto-threshold method\n"
833 " automatically perform image thresholding\n"
834 " -bench iterations measure performance\n"
835 " -bilateral-blur geometry\n"
836 " non-linear, edge-preserving, and noise-reducing smoothing filter\n"
837 " -black-threshold value\n"
838 " force all pixels below the threshold into black\n"
839 " -blue-shift factor simulate a scene at nighttime in the moonlight\n"
840 " -blur geometry reduce image noise and reduce detail levels\n"
841 " -border geometry surround image with a border of color\n"
842 " -bordercolor color border color\n"
843 " -brightness-contrast geometry\n"
844 " improve brightness / contrast of the image\n"
845 " -canny geometry detect edges in the image\n"
846 " -cdl filename color correct with a color decision list\n"
847 " -channel mask set the image channel mask\n"
848 " -charcoal radius simulate a charcoal drawing\n"
849 " -chop geometry remove pixels from the image interior\n"
850 " -clahe geometry contrast limited adaptive histogram equalization\n"
851 " -clamp keep pixel values in range (0-QuantumRange)\n"
852 " -colorize value colorize the image with the fill color\n"
853 " -color-matrix matrix apply color correction to the image\n"
854 " -colors value preferred number of colors in the image\n"
855 " -connected-components connectivity\n"
856 " connected-components uniquely labeled\n"
857 " -contrast enhance or reduce the image contrast\n"
858 " -contrast-stretch geometry\n"
859 " improve contrast by 'stretching' the intensity range\n"
860 " -convolve coefficients\n"
861 " apply a convolution kernel to the image\n"
862 " -cycle amount cycle the image colormap\n"
863 " -decipher filename convert cipher pixels to plain pixels\n"
864 " -deskew threshold straighten an image\n"
865 " -despeckle reduce the speckles within an image\n"
866 " -distort method args\n"
867 " distort images according to given method and args\n"
868 " -draw string annotate the image with a graphic primitive\n"
869 " -edge radius apply a filter to detect edges in the image\n"
870 " -encipher filename convert plain pixels to cipher pixels\n"
871 " -emboss radius emboss an image\n"
872 " -enhance apply a digital filter to enhance a noisy image\n"
873 " -equalize perform histogram equalization to an image\n"
874 " -evaluate operator value\n"
875 " evaluate an arithmetic, relational, or logical expression\n"
876 " -extent geometry set the image size\n"
877 " -extract geometry extract area from image\n"
878 " -fft implements the discrete Fourier transform (DFT)\n"
879 " -flip flip image vertically\n"
880 " -floodfill geometry color\n"
881 " floodfill the image with color\n"
882 " -flop flop image horizontally\n"
883 " -frame geometry surround image with an ornamental border\n"
884 " -function name parameters\n"
885 " apply function over image values\n"
886 " -gamma value level of gamma correction\n"
887 " -gaussian-blur geometry\n"
888 " reduce image noise and reduce detail levels\n"
889 " -geometry geometry preferred size or location of the image\n"
890 " -grayscale method convert image to grayscale\n"
891 " -hough-lines geometry\n"
892 " identify lines in the image\n"
893 " -identify identify the format and characteristics of the image\n"
894 " -ift implements the inverse discrete Fourier transform (DFT)\n"
895 " -implode amount implode image pixels about the center\n"
896 " -integral calculate the sum of values (pixel values) in the image\n"
897 " -interpolative-resize geometry\n"
898 " resize image using interpolation\n"
899 " -kmeans geometry K means color reduction\n"
900 " -kuwahara geometry edge preserving noise reduction filter\n"
901 " -lat geometry local adaptive thresholding\n"
902 " -level value adjust the level of image contrast\n"
903 " -level-colors color,color\n"
904 " level image with the given colors\n"
905 " -linear-stretch geometry\n"
906 " improve contrast by 'stretching with saturation'\n"
907 " -liquid-rescale geometry\n"
908 " rescale image with seam-carving\n"
909 " -local-contrast geometry\n"
910 " enhance local contrast\n"
911 " -mean-shift geometry delineate arbitrarily shaped clusters in the image\n"
912 " -median geometry apply a median filter to the image\n"
913 " -mode geometry make each pixel the 'predominant color' of the\n"
914 " neighborhood\n"
915 " -modulate value vary the brightness, saturation, and hue\n"
916 " -monochrome transform image to black and white\n"
917 " -morphology method kernel\n"
918 " apply a morphology method to the image\n"
919 " -motion-blur geometry\n"
920 " simulate motion blur\n"
921 " -negate replace every pixel with its complementary color \n"
922 " -noise geometry add or reduce noise in an image\n"
923 " -normalize transform image to span the full range of colors\n"
924 " -opaque color change this color to the fill color\n"
925 " -ordered-dither NxN\n"
926 " add a noise pattern to the image with specific\n"
927 " amplitudes\n"
928 " -paint radius simulate an oil painting\n"
929 " -perceptible epsilon\n"
930 " pixel value less than |epsilon| become epsilon or\n"
931 " -epsilon\n"
932 " -polaroid angle simulate a Polaroid picture\n"
933 " -posterize levels reduce the image to a limited number of color levels\n"
934 " -profile filename add, delete, or apply an image profile\n"
935 " -quantize colorspace reduce colors in this colorspace\n"
936 " -raise value lighten/darken image edges to create a 3-D effect\n"
937 " -random-threshold low,high\n"
938 " random threshold the image\n"
939 " -range-threshold values\n"
940 " perform either hard or soft thresholding within some range of values in an image\n"
941 " -region geometry apply options to a portion of the image\n"
942 " -render render vector graphics\n"
943 " -resample geometry change the resolution of an image\n"
944 " -reshape geometry reshape the image\n"
945 " -resize geometry resize the image\n"
946 " -roll geometry roll an image vertically or horizontally\n"
947 " -rotate degrees apply Paeth rotation to the image\n"
948 " -rotational-blur angle\n"
949 " rotational blur the image\n"
950 " -sample geometry scale image with pixel sampling\n"
951 " -scale geometry scale the image\n"
952 " -segment values segment an image\n"
953 " -selective-blur geometry\n"
954 " selectively blur pixels within a contrast threshold\n"
955 " -sepia-tone threshold\n"
956 " simulate a sepia-toned photo\n"
957 " -set property value set an image property\n"
958 " -shade degrees shade the image using a distant light source\n"
959 " -shadow geometry simulate an image shadow\n"
960 " -sharpen geometry sharpen the image\n"
961 " -shave geometry shave pixels from the image edges\n"
962 " -shear geometry slide one edge of the image along the X or Y axis\n"
963 " -sigmoidal-contrast geometry\n"
964 " increase the contrast without saturating highlights or\n"
965 " shadows\n"
966 " -sketch geometry simulate a pencil sketch\n"
967 " -solarize threshold negate all pixels above the threshold level\n"
968 " -sort-pixels sort each scanline in ascending order of intensity\n"
969 " -sparse-color method args\n"
970 " fill in a image based on a few color points\n"
971 " -splice geometry splice the background color into the image\n"
972 " -spread radius displace image pixels by a random amount\n"
973 " -statistic type geometry\n"
974 " replace each pixel with corresponding statistic from the\n"
975 " neighborhood\n"
976 " -strip strip image of all profiles and comments\n"
977 " -swirl degrees swirl image pixels about the center\n"
978 " -threshold value threshold the image\n"
979 " -thumbnail geometry create a thumbnail of the image\n"
980 " -tile filename tile image when filling a graphic primitive\n"
981 " -tint value tint the image with the fill color\n"
982 " -transform affine transform image\n"
983 " -transparent color make this color transparent within the image\n"
984 " -transpose flip image vertically and rotate 90 degrees\n"
985 " -transverse flop image horizontally and rotate 270 degrees\n"
986 " -trim trim image edges\n"
987 " -type type image type\n"
988 " -unique-colors discard all but one of any pixel color\n"
989 " -unsharp geometry sharpen the image\n"
990 " -vignette geometry soften the edges of the image in vignette style\n"
991 " -wave geometry alter an image along a sine wave\n"
992 " -wavelet-denoise threshold\n"
993 " removes noise from the image using a wavelet transform\n"
994 " -white-balance automagically adjust white balance of image\n"
995 " -white-threshold value\n"
996 " force all pixels above the threshold into white",
997 sequence_operators[] =
998 " -append append an image sequence\n"
999 " -clut apply a color lookup table to the image\n"
1000 " -coalesce merge a sequence of images\n"
1001 " -combine combine a sequence of images\n"
1002 " -compare mathematically and visually annotate the difference between an image and its reconstruction\n"
1003 " -complex operator perform complex mathematics on an image sequence\n"
1004 " -composite composite image\n"
1005 " -copy geometry offset\n"
1006 " copy pixels from one area of an image to another\n"
1007 " -crop geometry cut out a rectangular region of the image\n"
1008 " -deconstruct break down an image sequence into constituent parts\n"
1009 " -evaluate-sequence operator\n"
1010 " evaluate an arithmetic, relational, or logical expression\n"
1011 " -flatten flatten a sequence of images\n"
1012 " -fx expression apply mathematical expression to an image channel(s)\n"
1013 " -hald-clut apply a Hald color lookup table to the image\n"
1014 " -layers method optimize, merge, or compare image layers\n"
1015 " -morph value morph an image sequence\n"
1016 " -mosaic create a mosaic from an image sequence\n"
1017 " -poly terms build a polynomial from the image sequence and the corresponding\n"
1018 " terms (coefficients and degree pairs).\n"
1019 " -print string interpret string and print to console\n"
1020 " -process arguments process the image with a custom image filter\n"
1021 " -smush geometry smush an image sequence together\n"
1022 " -write filename write images to this file",
1023 settings[] =
1024 " -adjoin join images into a single multi-image file\n"
1025 " -affine matrix affine transform matrix\n"
1026 " -alpha option activate, deactivate, reset, or set the alpha channel\n"
1027 " -antialias remove pixel-aliasing\n"
1028 " -authenticate password\n"
1029 " decipher image with this password\n"
1030 " -attenuate value lessen (or intensify) when adding noise to an image\n"
1031 " -background color background color\n"
1032 " -bias value add bias when convolving an image\n"
1033 " -black-point-compensation\n"
1034 " use black point compensation\n"
1035 " -blue-primary point chromaticity blue primary point\n"
1036 " -bordercolor color border color\n"
1037 " -caption string assign a caption to an image\n"
1038 " -clip clip along the first path from the 8BIM profile\n"
1039 " -clip-mask filename associate a clip mask with the image\n"
1040 " -clip-path id clip along a named path from the 8BIM profile\n"
1041 " -colorspace type alternate image colorspace\n"
1042 " -comment string annotate image with comment\n"
1043 " -compose operator set image composite operator\n"
1044 " -compress type type of pixel compression when writing the image\n"
1045 " -define format:option\n"
1046 " define one or more image format options\n"
1047 " -delay value display the next image after pausing\n"
1048 " -density geometry horizontal and vertical density of the image\n"
1049 " -depth value image depth\n"
1050 " -direction type render text right-to-left or left-to-right\n"
1051 " -display server get image or font from this X server\n"
1052 " -dispose method layer disposal method\n"
1053 " -dither method apply error diffusion to image\n"
1054 " -encoding type text encoding type\n"
1055 " -endian type endianness (MSB or LSB) of the image\n"
1056 " -family name render text with this font family\n"
1057 " -features distance analyze image features (e.g. contrast, correlation)\n"
1058 " -fill color color to use when filling a graphic primitive\n"
1059 " -filter type use this filter when resizing an image\n"
1060 " -font name render text with this font\n"
1061 " -format \"string\" output formatted image characteristics\n"
1062 " -fuzz distance colors within this distance are considered equal\n"
1063 " -gravity type horizontal and vertical text placement\n"
1064 " -green-primary point chromaticity green primary point\n"
1065 " -illuminant type reference illuminant\n"
1066 " -intensity method method to generate an intensity value from a pixel\n"
1067 " -intent type type of rendering intent when managing the image color\n"
1068 " -interlace type type of image interlacing scheme\n"
1069 " -interline-spacing value\n"
1070 " set the space between two text lines\n"
1071 " -interpolate method pixel color interpolation method\n"
1072 " -interword-spacing value\n"
1073 " set the space between two words\n"
1074 " -kerning value set the space between two letters\n"
1075 " -label string assign a label to an image\n"
1076 " -limit type value pixel cache resource limit\n"
1077 " -loop iterations add Netscape loop extension to your GIF animation\n"
1078 " -matte store matte channel if the image has one\n"
1079 " -mattecolor color frame color\n"
1080 " -moments report image moments\n"
1081 " -monitor monitor progress\n"
1082 " -orient type image orientation\n"
1083 " -page geometry size and location of an image canvas (setting)\n"
1084 " -ping efficiently determine image attributes\n"
1085 " -pointsize value font point size\n"
1086 " -precision value maximum number of significant digits to print\n"
1087 " -preview type image preview type\n"
1088 " -quality value JPEG/MIFF/PNG compression level\n"
1089 " -quiet suppress all warning messages\n"
1090 " -read-mask filename associate a read mask with the image\n"
1091 " -red-primary point chromaticity red primary point\n"
1092 " -regard-warnings pay attention to warning messages\n"
1093 " -remap filename transform image colors to match this set of colors\n"
1094 " -repage geometry size and location of an image canvas\n"
1095 " -respect-parentheses settings remain in effect until parenthesis boundary\n"
1096 " -sampling-factor geometry\n"
1097 " horizontal and vertical sampling factor\n"
1098 " -scene value image scene number\n"
1099 " -seed value seed a new sequence of pseudo-random numbers\n"
1100 " -size geometry width and height of image\n"
1101 " -stretch type render text with this font stretch\n"
1102 " -stroke color graphic primitive stroke color\n"
1103 " -strokewidth value graphic primitive stroke width\n"
1104 " -style type render text with this font style\n"
1105 " -support factor resize support: > 1.0 is blurry, < 1.0 is sharp\n"
1106 " -synchronize synchronize image to storage device\n"
1107 " -taint declare the image as modified\n"
1108 " -texture filename name of texture to tile onto the image background\n"
1109 " -tile-offset geometry\n"
1110 " tile offset\n"
1111 " -treedepth value color tree depth\n"
1112 " -transparent-color color\n"
1113 " transparent color\n"
1114 " -undercolor color annotation bounding box color\n"
1115 " -units type the units of image resolution\n"
1116 " -verbose print detailed information about the image\n"
1117 " -view FlashPix viewing transforms\n"
1118 " -virtual-pixel method\n"
1119 " virtual pixel access method\n"
1120 " -weight type render text with this font weight\n"
1121 " -white-point point chromaticity white point\n"
1122 " -write-mask filename associate a write mask with the image"
1123 " -word-break type sets whether line breaks appear wherever the text would otherwise overflow",
1124 stack_operators[] =
1125 " -clone indexes clone an image\n"
1126 " -delete indexes delete the image from the image sequence\n"
1127 " -duplicate count,indexes\n"
1128 " duplicate an image one or more times\n"
1129 " -insert index insert last image into the image sequence\n"
1130 " -reverse reverse image sequence\n"
1131 " -swap indexes swap two images in the image sequence";
1132
1133 ListMagickVersion(stdout);
1134 (void) FormatLocaleFile(stdout,
1135 "Usage: %s tool [ {option} | {image} ... ] {output_image}\n",
1136 GetClientName());
1137 (void) FormatLocaleFile(stdout,
1138 "Usage: %s [ {option} | {image} ... ] {output_image}\n",GetClientName());
1139 (void) FormatLocaleFile(stdout,
1140 " %s [ {option} | {image} ... ] -script {filename} [ {script_args} ...]\n",
1141 GetClientName());
1142 (void) FormatLocaleFile(stdout,"\nImage Settings:\n");
1143 (void) FormatLocaleFile(stdout,"%s\n",settings);
1144 (void) FormatLocaleFile(stdout,"\nImage Operators:\n");
1145 (void) FormatLocaleFile(stdout,"%s\n",operators);
1146 (void) FormatLocaleFile(stdout,"\nImage Channel Operators:\n");
1147 (void) FormatLocaleFile(stdout,"%s\n",channel_operators);
1148 (void) FormatLocaleFile(stdout,"\nImage Sequence Operators:\n");
1149 (void) FormatLocaleFile(stdout,"%s\n",sequence_operators);
1150 (void) FormatLocaleFile(stdout,"\nImage Stack Operators:\n");
1151 (void) FormatLocaleFile(stdout,"%s\n",stack_operators);
1152 (void) FormatLocaleFile(stdout,"\nMiscellaneous Options:\n");
1153 (void) FormatLocaleFile(stdout,"%s\n",miscellaneous);
1154 (void) FormatLocaleFile(stdout,
1155 "\nBy default, the image format of 'file' is determined by its magic\n");
1156 (void) FormatLocaleFile(stdout,
1157 "number. To specify a particular image format, precede the filename\n");
1158 (void) FormatLocaleFile(stdout,
1159 "with an image format name and a colon (i.e. ps:image) or specify the\n");
1160 (void) FormatLocaleFile(stdout,
1161 "image type as the filename suffix (i.e. image.ps). Specify 'file' as\n");
1162 (void) FormatLocaleFile(stdout,"'-' for standard input or output.\n");
1163 return(MagickTrue);
1164}
1165
1166static void MagickUsage(MagickBooleanType verbose)
1167{
1168 const char
1169 *name;
1170
1171 size_t
1172 len;
1173
1174 name=GetClientName();
1175 len=strlen(name);
1176
1177 if (verbose == MagickFalse)
1178 {
1179 (void) MagickCommandUsage();
1180 return;
1181 }
1182
1183 if (len>=7 && LocaleCompare("convert",name+len-7) == 0) {
1184 /* convert usage */
1185 (void) FormatLocaleFile(stdout,
1186 "Usage: %s [ {option} | {image} ... ] {output_image}\n",name);
1187 (void) FormatLocaleFile(stdout,
1188 " %s -help | -version | -usage | -list {option}\n\n",name);
1189 return;
1190 }
1191 else if (len>=6 && LocaleCompare("script",name+len-6) == 0) {
1192 /* magick-script usage */
1193 (void) FormatLocaleFile(stdout,
1194 "Usage: %s {filename} [ {script_args} ... ]\n",name);
1195 }
1196 else {
1197 /* magick usage */
1198 (void) FormatLocaleFile(stdout,
1199 "Usage: %s tool [ {option} | {image} ... ] {output_image}\n",name);
1200 (void) FormatLocaleFile(stdout,
1201 "Usage: %s [ {option} | {image} ... ] {output_image}\n",name);
1202 (void) FormatLocaleFile(stdout,
1203 " %s [ {option} | {image} ... ] -script {filename} [ {script_args} ...]\n",
1204 name);
1205 }
1206 (void) FormatLocaleFile(stdout,
1207 " %s -help | -version | -usage | -list {option}\n\n",name);
1208
1209 (void) FormatLocaleFile(stdout,"%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
1210 "All options are performed in a strict 'as you see them' order\n",
1211 "You must read-in images before you can operate on them.\n",
1212 "\n",
1213 "Magick Script files can use any of the following forms...\n",
1214 " #!/path/to/magick -script\n",
1215 "or\n",
1216 " #!/bin/sh\n",
1217 " :; exec magick -script \"$0\" \"$@\"; exit 10\n",
1218 " # Magick script from here...\n",
1219 "or\n",
1220 " #!/usr/bin/env magick-script\n",
1221 "The latter two forms do not require the path to the command hard coded.\n",
1222 "Note: \"magick-script\" needs to be linked to the \"magick\" command.\n",
1223 "\n",
1224 "For more information on usage, options, examples, and techniques\n",
1225 "see the ImageMagick website at ", MagickAuthoritativeURL);
1226
1227 return;
1228}
1229
1230/*
1231 Concatenate given file arguments to the given output argument.
1232 Used for a special -concatenate option used for specific 'delegates'.
1233 The option is not formally documented.
1234
1235 magick -concatenate files... output
1236
1237 This is much like the UNIX "cat" command, but for both UNIX and Windows,
1238 however the last argument provides the output filename.
1239*/
1240static MagickBooleanType ConcatenateImages(int argc,char **argv,
1241 ExceptionInfo *exception )
1242{
1243 FILE
1244 *input,
1245 *output;
1246
1247 MagickBooleanType
1248 status;
1249
1250 int
1251 c;
1252
1253 ssize_t
1254 i;
1255
1256 if (ExpandFilenames(&argc,&argv) == MagickFalse)
1257 ThrowFileException(exception,ResourceLimitError,"MemoryAllocationFailed",
1258 argv[argc-1]);
1259 if (IsPathAuthorized(WritePolicyRights,argv[argc-1]) == MagickFalse)
1260 ThrowPolicyException(argv[argc-1],MagickFalse);
1261 output=fopen_utf8(argv[argc-1],"wb");
1262 if (output == (FILE *) NULL)
1263 {
1264 ThrowFileException(exception,FileOpenError,"UnableToOpenFile",
1265 argv[argc-1]);
1266 return(MagickFalse);
1267 }
1268 status=MagickTrue;
1269 for (i=2; i < ((ssize_t) argc-1); i++)
1270 {
1271 if (IsPathAuthorized(ReadPolicyRights,argv[i]) == MagickFalse)
1272 ThrowPolicyException(argv[i],MagickFalse);
1273 input=fopen_utf8(argv[i],"rb");
1274 if (input == (FILE *) NULL)
1275 {
1276 ThrowFileException(exception,FileOpenError,"UnableToOpenFile",argv[i]);
1277 continue;
1278 }
1279 for (c=fgetc(input); c != EOF; c=fgetc(input))
1280 if (fputc((char) c,output) != c)
1281 status=MagickFalse;
1282 (void) fclose(input);
1283 (void) remove_utf8(argv[i]);
1284 }
1285 (void) fclose(output);
1286 return(status);
1287}
1288
1289WandExport MagickBooleanType MagickImageCommand(ImageInfo *image_info,int argc,
1290 char **argv,char **metadata,ExceptionInfo *exception)
1291{
1292 MagickCLI
1293 *cli_wand;
1294
1295 size_t
1296 len;
1297
1298 assert(image_info != (ImageInfo *) NULL);
1299
1300 /* For specific OS command line requirements */
1301 ReadCommandlLine(argc,&argv);
1302
1303 /* Initialize special "CLI Wand" to hold images and settings (empty) */
1304 cli_wand=AcquireMagickCLI(image_info,exception);
1305 cli_wand->location="Initializing";
1306 cli_wand->filename=argv[0];
1307 cli_wand->line=1;
1308
1309 if (cli_wand->wand.debug != MagickFalse)
1310 (void) CLILogEvent(cli_wand,CommandEvent,GetMagickModule(),
1311 "\"%s\"",argv[0]);
1312
1313
1314 GetPathComponent(argv[0],TailPath,cli_wand->wand.name);
1315 (void) SetClientName(cli_wand->wand.name);
1316 (void) ConcatenateMagickString(cli_wand->wand.name,"-CLI",MagickPathExtent);
1317
1318 len=strlen(argv[0]); /* precaution */
1319
1320 /* "convert" command - give a "deprecated" warning" */
1321 if (len>=7 && LocaleCompare("convert",argv[0]+len-7) == 0) {
1322 cli_wand->process_flags = ConvertCommandOptionFlags;
1323 }
1324
1325 /* Special Case: If command name ends with "script" implied "-script" */
1326 if (len>=6 && LocaleCompare("script",argv[0]+len-6) == 0) {
1327 if (argc >= 2 && ( (*(argv[1]) != '-') || (strlen(argv[1]) == 1) )) {
1328 GetPathComponent(argv[1],TailPath,cli_wand->wand.name);
1329 ProcessScriptOptions(cli_wand,argv[1],argc,argv,2);
1330 goto Magick_Command_Cleanup;
1331 }
1332 }
1333
1334 /* Special Case: Version Information and Abort */
1335 if (argc == 2) {
1336 if ((LocaleCompare("-version",argv[1]) == 0) || /* GNU standard option */
1337 (LocaleCompare("--version",argv[1]) == 0) ) { /* just version */
1338 CLIOption(cli_wand, "-version");
1339 goto Magick_Command_Exit;
1340 }
1341 if ((LocaleCompare("-help",argv[1]) == 0) || /* GNU standard option */
1342 (LocaleCompare("--help",argv[1]) == 0) ) { /* just a brief summary */
1343 if (cli_wand->wand.debug != MagickFalse)
1344 (void) CLILogEvent(cli_wand,CommandEvent,GetMagickModule(),
1345 "- Special Option \"%s\"", argv[1]);
1346 MagickUsage(MagickFalse);
1347 goto Magick_Command_Exit;
1348 }
1349 if (LocaleCompare("-usage",argv[1]) == 0) { /* both version & usage */
1350 if (cli_wand->wand.debug != MagickFalse)
1351 (void) CLILogEvent(cli_wand,CommandEvent,GetMagickModule(),
1352 "- Special Option \"%s\"", argv[1]);
1353 CLIOption(cli_wand, "-version" );
1354 MagickUsage(MagickTrue);
1355 goto Magick_Command_Exit;
1356 }
1357 }
1358
1359 /* not enough arguments -- including -help */
1360 if (argc < 3) {
1361 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1362 "InvalidArgument","%s",argc > 1 ? argv[argc-1] : "");
1363 MagickUsage(MagickFalse);
1364 goto Magick_Command_Exit;
1365 }
1366
1367 /* Special "concatenate option (hidden) for delegate usage */
1368 if (LocaleCompare("-concatenate",argv[1]) == 0) {
1369 if (cli_wand->wand.debug != MagickFalse)
1370 (void) CLILogEvent(cli_wand,CommandEvent,GetMagickModule(),
1371 "- Special Option \"%s\"", argv[1]);
1372 (void) ConcatenateImages(argc,argv,exception);
1373 goto Magick_Command_Exit;
1374 }
1375
1376 /* List Information and Abort */
1377 if (argc == 3 && LocaleCompare("-list",argv[1]) == 0) {
1378 CLIOption(cli_wand, argv[1], argv[2]);
1379 goto Magick_Command_Exit;
1380 }
1381
1382 /* ------------- */
1383 /* The Main Call */
1384
1385 if (LocaleCompare("-script",argv[1]) == 0) {
1386 /* Start processing directly from script, no pre-script options
1387 Replace wand command name with script name
1388 First argument in the argv array is the script name to read.
1389 */
1390 GetPathComponent(argv[2],TailPath,cli_wand->wand.name);
1391 ProcessScriptOptions(cli_wand,argv[2],argc,argv,3);
1392 }
1393 else {
1394 /* Normal Command Line, assumes output file as last option */
1395 ProcessCommandOptions(cli_wand,argc,argv,1);
1396 }
1397 /* ------------- */
1398
1399Magick_Command_Cleanup:
1400 cli_wand->location="Cleanup";
1401 cli_wand->filename=argv[0];
1402 if (cli_wand->wand.debug != MagickFalse)
1403 (void) CLILogEvent(cli_wand,CommandEvent,GetMagickModule(),
1404 "\"%s\"",argv[0]);
1405
1406 /* recover original image_info and clean up stacks
1407 FUTURE: "-reset stacks" option */
1408 while ((cli_wand->image_list_stack != (CLIStack *) NULL) &&
1409 (cli_wand->image_list_stack->next != (CLIStack *) NULL))
1410 CLIOption(cli_wand,")");
1411 while ((cli_wand->image_info_stack != (CLIStack *) NULL) &&
1412 (cli_wand->image_info_stack->next != (CLIStack *) NULL))
1413 CLIOption(cli_wand,"}");
1414
1415Magick_Command_Exit:
1416 if ((cli_wand->wand.image_info != image_info) ||
1417 (cli_wand->wand.exception != exception))
1418 {
1419 CLIStack
1420 *node;
1421
1422 /*
1423 Pop image_info settings from stack.
1424 */
1425 node=(CLIStack *) cli_wand->image_info_stack;
1426 cli_wand->image_info_stack=node->next;
1427 (void) DestroyImageInfo(cli_wand->wand.image_info);
1428 cli_wand->wand.image_info=(ImageInfo *) node->data;
1429 node=(CLIStack *) RelinquishMagickMemory(node);
1430 }
1431
1432 /* Handle metadata for ImageMagickObject COM object for Windows VBS */
1433 if ((cli_wand->wand.images != (Image *) NULL) && (metadata != (char **) NULL))
1434 {
1435 const char
1436 *format;
1437
1438 char
1439 *text;
1440
1441 format="%w,%h,%m"; /* Get this from image_info Option splaytree */
1442 text=InterpretImageProperties(image_info,cli_wand->wand.images,format,
1443 exception);
1444 if (text == (char *) NULL)
1445 {
1446 char *message = GetExceptionMessage(errno);
1447 (void) ThrowMagickException(exception,GetMagickModule(),
1448 ResourceLimitError,"MemoryAllocationFailed","`%s'",message);
1449 message=DestroyString(message);
1450 }
1451 else
1452 {
1453 (void) ConcatenateString(&(*metadata),text);
1454 text=DestroyString(text);
1455 }
1456 }
1457
1458 cli_wand->location="Exiting";
1459 cli_wand->filename=argv[0];
1460 if (cli_wand->wand.debug != MagickFalse)
1461 (void) CLILogEvent(cli_wand,CommandEvent,GetMagickModule(),
1462 "\"%s\"",argv[0]);
1463
1464 /* Destroy the special CLI Wand */
1465 cli_wand->wand.image_info = (ImageInfo *) NULL; /* not these */
1466 cli_wand->wand.exception = (ExceptionInfo *) NULL;
1467 cli_wand=DestroyMagickCLI(cli_wand);
1468
1469 return(exception->severity < ErrorException ? MagickTrue : MagickFalse);
1470}