MagickWand 7.1.1
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/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/string-private.h"
58#include "MagickCore/thread-private.h"
59#include "MagickCore/utility-private.h"
60#include "MagickCore/exception-private.h"
61#include "MagickCore/version.h"
62
63/* verbose debugging,
64 0 - no debug lines
65 3 - show option details (better to use -debug Command now)
66 5 - image counts (after option runs)
67*/
68#define MagickCommandDebug 0
69
70/*
71%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
72% %
73% %
74% %
75% M a g i c k C o m m a n d G e n e s i s %
76% %
77% %
78% %
79%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
80%
81% MagickCommandGenesis() applies image processing options to an image as
82% prescribed by command line options.
83%
84% It wiil look for special options like "-debug", "-bench", and
85% "-distribute-cache" that needs to be applied even before the main
86% processing begins, and may completely overrule normal command processing.
87% Such 'Genesis' Options can only be given on the CLI, (not in a script)
88% and are typically ignored (as they have been handled) if seen later.
89%
90% The format of the MagickCommandGenesis method is:
91%
92% MagickBooleanType MagickCommandGenesis(ImageInfo *image_info,
93% MagickCommand command,int argc,char **argv,char **metadata,
94% ExceptionInfo *exception)
95%
96% A description of each parameter follows:
97%
98% o image_info: the image info.
99%
100% o command: Choose from ConvertImageCommand, IdentifyImageCommand,
101% MogrifyImageCommand, CompositeImageCommand, CompareImagesCommand,
102% ConjureImageCommand, StreamImageCommand, ImportImageCommand,
103% DisplayImageCommand, or AnimateImageCommand.
104%
105% o argc: Specifies a pointer to an integer describing the number of
106% elements in the argument vector.
107%
108% o argv: Specifies a pointer to a text array containing the command line
109% arguments.
110%
111% o metadata: any metadata is returned here.
112%
113% o exception: return any errors or warnings in this structure.
114%
115*/
116WandExport MagickBooleanType MagickCommandGenesis(ImageInfo *image_info,
117 MagickCommand command,int argc,char **argv,char **metadata,
118 ExceptionInfo *exception)
119{
120 char
121 client_name[MagickPathExtent],
122 *option;
123
124 double
125 duration,
126 serial;
127
128 MagickBooleanType
129 concurrent,
130 regard_warnings,
131 status;
132
133 size_t
134 iterations,
135 number_threads;
136
137 ssize_t
138 i,
139 n;
140
141 (void) setlocale(LC_ALL,"");
142 (void) setlocale(LC_NUMERIC,"C");
143 GetPathComponent(argv[0],TailPath,client_name);
144 (void) SetClientName(client_name);
145 concurrent=MagickFalse;
146 duration=(-1.0);
147 iterations=1;
148 status=MagickTrue;
149 regard_warnings=MagickFalse;
150 for (i=1; i < (ssize_t) (argc-1); i++)
151 {
152 option=argv[i];
153 if ((strlen(option) == 1) || ((*option != '-') && (*option != '+')))
154 continue;
155 if (LocaleCompare("-bench",option) == 0)
156 iterations=StringToUnsignedLong(argv[++i]);
157 if (LocaleCompare("-concurrent",option) == 0)
158 concurrent=MagickTrue;
159 if (LocaleCompare("-debug",option) == 0)
160 (void) SetLogEventMask(argv[++i]);
161 if (LocaleCompare("-distribute-cache",option) == 0)
162 {
163 DistributePixelCacheServer(StringToInteger(argv[++i]),exception);
164 exit(0);
165 }
166 if (LocaleCompare("-duration",option) == 0)
167 duration=StringToDouble(argv[++i],(char **) NULL);
168 if (LocaleCompare("-regard-warnings",option) == 0)
169 regard_warnings=MagickTrue;
170 }
171 if (iterations == 1)
172 {
173 char
174 *text;
175
176 text=(char *) NULL;
177 status=command(image_info,argc,argv,&text,exception);
178 if (exception->severity != UndefinedException)
179 {
180 if ((exception->severity > ErrorException) ||
181 (regard_warnings != MagickFalse))
182 status=MagickFalse;
183 CatchException(exception);
184 }
185 if (text != (char *) NULL)
186 {
187 if (metadata != (char **) NULL)
188 (void) ConcatenateString(&(*metadata),text);
189 text=DestroyString(text);
190 }
191 return(status);
192 }
193 number_threads=GetOpenMPMaximumThreads();
194 serial=0.0;
195 for (n=1; n <= (ssize_t) number_threads; n++)
196 {
197 double
198 e,
199 parallel,
200 user_time;
201
202 TimerInfo
203 *timer;
204
205 (void) SetMagickResourceLimit(ThreadResource,(MagickSizeType) n);
206 timer=AcquireTimerInfo();
207 if (concurrent == MagickFalse)
208 {
209 for (i=0; i < (ssize_t) iterations; i++)
210 {
211 char
212 *text;
213
214 text=(char *) NULL;
215 if (status == MagickFalse)
216 continue;
217 if (duration > 0)
218 {
219 if (GetElapsedTime(timer) > duration)
220 continue;
221 (void) ContinueTimer(timer);
222 }
223 status=command(image_info,argc,argv,&text,exception);
224 if (exception->severity != UndefinedException)
225 {
226 if ((exception->severity > ErrorException) ||
227 (regard_warnings != MagickFalse))
228 status=MagickFalse;
229 CatchException(exception);
230 }
231 if (text != (char *) NULL)
232 {
233 if (metadata != (char **) NULL)
234 (void) ConcatenateString(&(*metadata),text);
235 text=DestroyString(text);
236 }
237 }
238 }
239 else
240 {
241 SetOpenMPNested(1);
242#if defined(MAGICKCORE_OPENMP_SUPPORT)
243 # pragma omp parallel for shared(status)
244#endif
245 for (i=0; i < (ssize_t) iterations; i++)
246 {
247 char
248 *text;
249
250 text=(char *) NULL;
251 if (status == MagickFalse)
252 continue;
253 if (duration > 0)
254 {
255 if (GetElapsedTime(timer) > duration)
256 continue;
257 (void) ContinueTimer(timer);
258 }
259 status=command(image_info,argc,argv,&text,exception);
260#if defined(MAGICKCORE_OPENMP_SUPPORT)
261 # pragma omp critical (MagickCore_MagickCommandGenesis)
262#endif
263 {
264 if (exception->severity != UndefinedException)
265 {
266 if ((exception->severity > ErrorException) ||
267 (regard_warnings != MagickFalse))
268 status=MagickFalse;
269 CatchException(exception);
270 }
271 if (text != (char *) NULL)
272 {
273 if (metadata != (char **) NULL)
274 (void) ConcatenateString(&(*metadata),text);
275 text=DestroyString(text);
276 }
277 }
278 }
279 }
280 user_time=GetUserTime(timer);
281 parallel=GetElapsedTime(timer);
282 e=1.0;
283 if (n == 1)
284 serial=parallel;
285 else
286 e=((1.0/(1.0/((serial/(serial+parallel))+(1.0-(serial/(serial+parallel)))/
287 (double) n)))-(1.0/(double) n))/(1.0-1.0/(double) n);
288 (void) FormatLocaleFile(stderr,
289 " Performance[%.20g]: %.20gi %0.3fips %0.6fe %0.6fu %lu:%02lu.%03lu\n",
290 (double) n,(double) iterations,(double) iterations/parallel,e,user_time,
291 (unsigned long) (parallel/60.0),(unsigned long) floor(fmod(parallel,
292 60.0)),(unsigned long) (1000.0*(parallel-floor(parallel))+0.5));
293 timer=DestroyTimerInfo(timer);
294 }
295 return(status);
296}
297
298/*
299%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
300% %
301% %
302% %
303+ P r o c e s s S c r i p t O p t i o n s %
304% %
305% %
306% %
307%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
308%
309% ProcessScriptOptions() reads options and processes options as they are
310% found in the given file, or pipeline. The filename to open and read
311% options is given as the 'index' argument of the argument array given.
312%
313% Other arguments following index may be read by special script options
314% as settings (strings), images, or as operations to be processed in various
315% ways. How they are treated is up to the script being processed.
316%
317% Note that a script not 'return' to the command line processing, nor can
318% they call (and return from) other scripts. At least not at this time.
319%
320% There are no 'ProcessOptionFlags' control flags at this time.
321%
322% The format of the ProcessScriptOptions method is:
323%
324% void ProcessScriptOptions(MagickCLI *cli_wand,const char *filename,
325% int argc,char **argv,int index)
326%
327% A description of each parameter follows:
328%
329% o cli_wand: the main CLI Wand to use.
330%
331% o filename: the filename of script to process
332%
333% o argc: the number of elements in the argument vector. (optional)
334%
335% o argv: A text array containing the command line arguments. (optional)
336%
337% o index: offset of next argument in argv (script arguments) (optional)
338%
339*/
340WandExport void ProcessScriptOptions(MagickCLI *cli_wand,const char *filename,
341 int magick_unused(argc),char **magick_unused(argv),int magick_unused(index))
342{
344 *token_info;
345
346 CommandOptionFlags
347 option_type;
348
349 int
350 count;
351
352 char
353 *option,
354 *arg1,
355 *arg2;
356
357 magick_unreferenced(argc);
358 magick_unreferenced(argv);
359 magick_unreferenced(index);
360 assert(filename != (char *) NULL ); /* at least one argument - script name */
361 assert(cli_wand != (MagickCLI *) NULL);
362 assert(cli_wand->signature == MagickWandSignature);
363 if (cli_wand->wand.debug != MagickFalse)
364 (void) LogMagickEvent(CommandEvent,GetMagickModule(),
365 "Processing script \"%s\"", filename);
366
367 /* open file script or stream, and set up tokenizer */
368 token_info = AcquireScriptTokenInfo(filename);
369 if (token_info == (ScriptTokenInfo *) NULL) {
370 CLIWandExceptionFile(OptionFatalError,"UnableToOpenScript",filename);
371 return;
372 }
373
374 /* define the error location string for use in exceptions
375 order of location format escapes: filename, line, column */
376 cli_wand->location="in \"%s\" at line %u,column %u";
377 if ( LocaleCompare("-", filename) == 0 )
378 cli_wand->filename="stdin";
379 else
380 cli_wand->filename=filename;
381
382 /* Process Options from Script */
383 option = arg1 = arg2 = (char*) NULL;
384DisableMSCWarning(4127)
385 while (1) {
386RestoreMSCWarning
387
388 { MagickBooleanType status = GetScriptToken(token_info);
389 cli_wand->line=token_info->token_line;
390 cli_wand->column=token_info->token_column;
391 if (status == MagickFalse)
392 break; /* error or end of options */
393 }
394
395 do { /* use break to loop to exception handler and loop */
396
397 /* save option details */
398 CloneString(&option,token_info->token);
399
400 /* get option, its argument count, and option type */
401 cli_wand->command = GetCommandOptionInfo(option);
402 count=cli_wand->command->type;
403 option_type=(CommandOptionFlags) cli_wand->command->flags;
404#if 0
405 (void) FormatLocaleFile(stderr, "Script: %u,%u: \"%s\" matched \"%s\"\n",
406 cli_wand->line, cli_wand->line, option, cli_wand->command->mnemonic );
407#endif
408
409 /* handle a undefined option - image read - always for "magick-script" */
410 if ( option_type == UndefinedOptionFlag ||
411 (option_type & NonMagickOptionFlag) != 0 ) {
412#if MagickCommandDebug >= 3
413 (void) FormatLocaleFile(stderr, "Script %u,%u Non-Option: \"%s\"\n",
414 cli_wand->line, cli_wand->line, option);
415#endif
416 if (IsCommandOption(option) == MagickFalse) {
417 /* non-option -- treat as a image read */
418 cli_wand->command=(const OptionInfo *) NULL;
419 CLIOption(cli_wand,"-read",option);
420 break; /* next option */
421 }
422 CLIWandException(OptionFatalError,"UnrecognizedOption",option);
423 break; /* next option */
424 }
425
426 if ( count >= 1 ) {
427 if (GetScriptToken(token_info) == MagickFalse)
428 CLIWandException(OptionFatalError,"MissingArgument",option);
429 CloneString(&arg1,token_info->token);
430 }
431 else
432 CloneString(&arg1,(char *) NULL);
433
434 if ( count >= 2 ) {
435 if (GetScriptToken(token_info) == MagickFalse)
436 CLIWandExceptionBreak(OptionFatalError,"MissingArgument",option);
437 CloneString(&arg2,token_info->token);
438 }
439 else
440 CloneString(&arg2,(char *) NULL);
441
442 /*
443 Process Options
444 */
445#if MagickCommandDebug >= 3
446 (void) FormatLocaleFile(stderr,
447 "Script %u,%u Option: \"%s\" Count: %d Flags: %04x Args: \"%s\" \"%s\"\n",
448 cli_wand->line,cli_wand->line,option,count,option_type,arg1,arg2);
449#endif
450 /* Hard Deprecated Options, no code to execute - error */
451 if ( (option_type & DeprecateOptionFlag) != 0 ) {
452 CLIWandException(OptionError,"DeprecatedOptionNoCode",option);
453 break; /* next option */
454 }
455
456 /* MagickCommandGenesis() options have no place in a magick script */
457 if ( (option_type & GenesisOptionFlag) != 0 ) {
458 CLIWandException(OptionError,"InvalidUseOfOption",option);
459 break; /* next option */
460 }
461
462 /* handle any special 'script' options */
463 if ( (option_type & SpecialOptionFlag) != 0 ) {
464 if ( LocaleCompare(option,"-exit") == 0 ) {
465 goto loop_exit; /* break out of loop - return from script */
466 }
467 if ( LocaleCompare(option,"-script") == 0 ) {
468 /* FUTURE: call new script from this script - error for now */
469 CLIWandException(OptionError,"InvalidUseOfOption",option);
470 break; /* next option */
471 }
472 /* FUTURE: handle special script-argument options here */
473 /* handle any other special operators now */
474 CLIWandException(OptionError,"InvalidUseOfOption",option);
475 break; /* next option */
476 }
477
478 /* Process non-specific Option */
479 CLIOption(cli_wand, option, arg1, arg2);
480 (void) fflush(stdout);
481 (void) fflush(stderr);
482
483DisableMSCWarning(4127)
484 } while (0); /* break block to next option */
485RestoreMSCWarning
486
487#if MagickCommandDebug >= 5
488 fprintf(stderr, "Script Image Count = %ld\n",
489 GetImageListLength(cli_wand->wand.images) );
490#endif
491 if (CLICatchException(cli_wand, MagickFalse) != MagickFalse)
492 break; /* exit loop */
493 }
494
495 /*
496 Loop exit - check for some tokenization error
497 */
498loop_exit:
499#if MagickCommandDebug >= 3
500 (void) FormatLocaleFile(stderr, "Script End: %d\n", token_info->status);
501#endif
502 switch( token_info->status ) {
503 case TokenStatusOK:
504 case TokenStatusEOF:
505 if (cli_wand->image_list_stack != (CLIStack *) NULL)
506 CLIWandException(OptionError,"UnbalancedParenthesis", "(eof)");
507 else if (cli_wand->image_info_stack != (CLIStack *) NULL)
508 CLIWandException(OptionError,"UnbalancedBraces", "(eof)");
509 break;
510 case TokenStatusBadQuotes:
511 /* Ensure last token has a sane length for error report */
512 if( strlen(token_info->token) > INITAL_TOKEN_LENGTH-1 ) {
513 token_info->token[INITAL_TOKEN_LENGTH-4] = '.';
514 token_info->token[INITAL_TOKEN_LENGTH-3] = '.';
515 token_info->token[INITAL_TOKEN_LENGTH-2] = '.';
516 token_info->token[INITAL_TOKEN_LENGTH-1] = '\0';
517 }
518 CLIWandException(OptionFatalError,"ScriptUnbalancedQuotes",
519 token_info->token);
520 break;
521 case TokenStatusMemoryFailed:
522 CLIWandException(OptionFatalError,"ScriptTokenMemoryFailed","");
523 break;
524 case TokenStatusBinary:
525 CLIWandException(OptionFatalError,"ScriptIsBinary","");
526 break;
527 }
528 (void) fflush(stdout);
529 (void) fflush(stderr);
530 if (cli_wand->wand.debug != MagickFalse)
531 (void) LogMagickEvent(CommandEvent,GetMagickModule(),
532 "Script End \"%s\"", filename);
533
534 /* Clean up */
535 token_info = DestroyScriptTokenInfo(token_info);
536
537 CloneString(&option,(char *) NULL);
538 CloneString(&arg1,(char *) NULL);
539 CloneString(&arg2,(char *) NULL);
540
541 return;
542}
543
544/*
545%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
546% %
547% %
548% %
549+ P r o c e s s C o m m a n d O p t i o n s %
550% %
551% %
552% %
553%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
554%
555% ProcessCommandOptions() reads and processes arguments in the given
556% command line argument array. The 'index' defines where in the array we
557% should begin processing
558%
559% The 'process_flags' can be used to control and limit option processing.
560% For example, to only process one option, or how unknown and special options
561% are to be handled, and if the last argument in array is to be regarded as a
562% final image write argument (filename or special coder).
563%
564% The format of the ProcessCommandOptions method is:
565%
566% int ProcessCommandOptions(MagickCLI *cli_wand,int argc,char **argv,
567% int index)
568%
569% A description of each parameter follows:
570%
571% o cli_wand: the main CLI Wand to use.
572%
573% o argc: the number of elements in the argument vector.
574%
575% o argv: A text array containing the command line arguments.
576%
577% o process_flags: What type of arguments will be processed, ignored
578% or return errors.
579%
580% o index: index in the argv array to start processing from
581%
582% The function returns the index ot the next option to be processed. This
583% is really only relevant if process_flags contains a ProcessOneOptionOnly
584% flag.
585%
586*/
587WandExport int ProcessCommandOptions(MagickCLI *cli_wand,int argc,char **argv,
588 int index)
589{
590 const char
591 *option,
592 *arg1,
593 *arg2;
594
595 int
596 i,
597 end,
598 count;
599
600 CommandOptionFlags
601 option_type;
602
603 assert(argc>=index); /* you may have no arguments left! */
604 assert(argv != (char **) NULL);
605 assert(argv[index] != (char *) NULL);
606 assert(argv[argc-1] != (char *) NULL);
607 assert(cli_wand != (MagickCLI *) NULL);
608 assert(cli_wand->signature == MagickWandSignature);
609
610 /* define the error location string for use in exceptions
611 order of location format escapes: filename, line, column */
612 cli_wand->location="at %s arg %u";
613 cli_wand->filename="CLI";
614 cli_wand->line=(size_t) index; /* note first argument we will process */
615
616 if (cli_wand->wand.debug != MagickFalse)
617 (void) CLILogEvent(cli_wand,CommandEvent,GetMagickModule(),
618 "- Starting (\"%s\")", argv[index]);
619
620 end = argc;
621 if ( (cli_wand->process_flags & ProcessImplicitWrite) != 0 )
622 end--; /* the last argument is an implied write, do not process directly */
623
624 for (i=index; i < end; i += count +1) {
625 /* Finished processing one option? */
626 if ( (cli_wand->process_flags & ProcessOneOptionOnly) != 0 && i != index )
627 return(i);
628
629 do { /* use break to loop to exception handler and loop */
630
631 option=argv[i];
632 cli_wand->line=(size_t) i; /* note the argument for this option */
633
634 /* get option, its argument count, and option type */
635 cli_wand->command = GetCommandOptionInfo(argv[i]);
636 count=cli_wand->command->type;
637 option_type=(CommandOptionFlags) cli_wand->command->flags;
638#if 0
639 (void) FormatLocaleFile(stderr, "CLI %d: \"%s\" matched \"%s\"\n",
640 i, argv[i], cli_wand->command->mnemonic );
641#endif
642
643 if ( option_type == UndefinedOptionFlag ||
644 (option_type & NonMagickOptionFlag) != 0 ) {
645#if MagickCommandDebug >= 3
646 (void) FormatLocaleFile(stderr, "CLI arg %d Non-Option: \"%s\"\n",
647 i, option);
648#endif
649 if (IsCommandOption(option) == MagickFalse) {
650 if ( (cli_wand->process_flags & ProcessImplicitRead) != 0 ) {
651 /* non-option -- treat as a image read */
652 cli_wand->command=(const OptionInfo *) NULL;
653 CLIOption(cli_wand,"-read",option);
654 break; /* next option */
655 }
656 }
657 CLIWandException(OptionFatalError,"UnrecognizedOption",option);
658 break; /* next option */
659 }
660
661 if ( ((option_type & SpecialOptionFlag) != 0 ) &&
662 ((cli_wand->process_flags & ProcessScriptOption) != 0) &&
663 (LocaleCompare(option,"-script") == 0) ) {
664 /* Call Script from CLI, with a filename as a zeroth argument.
665 NOTE: -script may need to use the 'implicit write filename' argument
666 so it must be handled specially to prevent a 'missing argument' error.
667 */
668 if ( (i+count) >= argc )
669 CLIWandException(OptionFatalError,"MissingArgument",option);
670 ProcessScriptOptions(cli_wand,argv[i+1],argc,argv,i+count);
671 return(argc); /* Script does not return to CLI -- Yet */
672 /* FUTURE: when it does, their may be no write arg! */
673 }
674
675 if ((i+count) >= end ) {
676 CLIWandException(OptionFatalError,"MissingArgument",option);
677 if ( CLICatchException(cli_wand, MagickFalse) != MagickFalse )
678 return(end);
679 break; /* next option - not that their is any! */
680 }
681
682 arg1 = ( count >= 1 ) ? argv[i+1] : (char *) NULL;
683 arg2 = ( count >= 2 ) ? argv[i+2] : (char *) NULL;
684
685 /*
686 Process Known Options
687 */
688#if MagickCommandDebug >= 3
689 (void) FormatLocaleFile(stderr,
690 "CLI arg %u Option: \"%s\" Count: %d Flags: %04x Args: \"%s\" \"%s\"\n",
691 i,option,count,option_type,arg1,arg2);
692#endif
693 /* ignore 'genesis options' in command line args */
694 if ( (option_type & GenesisOptionFlag) != 0 )
695 break; /* next option */
696
697 /* Handle any special options for CLI (-script handled above) */
698 if ( (option_type & SpecialOptionFlag) != 0 ) {
699 if ( (cli_wand->process_flags & ProcessExitOption) != 0
700 && LocaleCompare(option,"-exit") == 0 )
701 return(i+count);
702 break; /* next option */
703 }
704
705 /* Process standard image option */
706 CLIOption(cli_wand, option, arg1, arg2);
707
708DisableMSCWarning(4127)
709 } while (0); /* break block to next option */
710RestoreMSCWarning
711
712#if MagickCommandDebug >= 5
713 (void) FormatLocaleFile(stderr, "CLI-post Image Count = %ld\n",
714 (long) GetImageListLength(cli_wand->wand.images) );
715#endif
716 if ( CLICatchException(cli_wand, MagickFalse) != MagickFalse )
717 return(i+count);
718 }
719 assert(i==end);
720
721 if ( (cli_wand->process_flags & ProcessImplicitWrite) == 0 )
722 return(end); /* no implied write -- just return to caller */
723
724 assert(end==argc-1); /* end should not include last argument */
725
726 /*
727 Implicit Write of images to final CLI argument
728 */
729 option=argv[i];
730 cli_wand->line=(size_t) i;
731
732 /* check that stacks are empty - or cause exception */
733 if (cli_wand->image_list_stack != (CLIStack *) NULL)
734 CLIWandException(OptionError,"UnbalancedParenthesis", "(end of cli)");
735 else if (cli_wand->image_info_stack != (CLIStack *) NULL)
736 CLIWandException(OptionError,"UnbalancedBraces", "(end of cli)");
737 if ( CLICatchException(cli_wand, MagickFalse) != MagickFalse )
738 return(argc);
739
740#if MagickCommandDebug >= 3
741 (void) FormatLocaleFile(stderr,"CLI arg %d Write File: \"%s\"\n",i,option);
742#endif
743
744 /* Valid 'do no write' replacement option (instead of "null:") */
745 if (LocaleCompare(option,"-exit") == 0 )
746 return(argc); /* just exit, no image write */
747
748 /* If filename looks like an option,
749 Or the common 'end of line' error of a single space.
750 -- produce an error */
751 if (IsCommandOption(option) != MagickFalse ||
752 (option[0] == ' ' && option[1] == '\0') ) {
753 CLIWandException(OptionError,"MissingOutputFilename",option);
754 return(argc);
755 }
756
757 cli_wand->command=(const OptionInfo *) NULL;
758 CLIOption(cli_wand,"-write",option);
759 return(argc);
760}
761
762/*
763%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
764% %
765% %
766% %
767+ M a g i c k I m a g e C o m m a n d %
768% %
769% %
770% %
771%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
772%
773% MagickImageCommand() Handle special use CLI arguments and prepare a
774% CLI MagickCLI to process the command line or directly specified script.
775%
776% This is essentially interface function between the MagickCore library
777% initialization function MagickCommandGenesis(), and the option MagickCLI
778% processing functions ProcessCommandOptions() or ProcessScriptOptions()
779%
780% The format of the MagickImageCommand method is:
781%
782% MagickBooleanType MagickImageCommand(ImageInfo *image_info,int argc,
783% char **argv,char **metadata,ExceptionInfo *exception)
784%
785% A description of each parameter follows:
786%
787% o image_info: the starting image_info structure
788% (for compatibility with MagickCommandGenisis())
789%
790% o argc: the number of elements in the argument vector.
791%
792% o argv: A text array containing the command line arguments.
793%
794% o metadata: any metadata (for VBS) is returned here.
795% (for compatibility with MagickCommandGenisis())
796%
797% o exception: return any errors or warnings in this structure.
798%
799*/
800
801static void MagickUsage(MagickBooleanType verbose)
802{
803 const char
804 *name;
805
806 size_t
807 len;
808
809 name=GetClientName();
810 len=strlen(name);
811
812 if (len>=7 && LocaleCompare("convert",name+len-7) == 0) {
813 /* convert usage */
814 (void) FormatLocaleFile(stdout,
815 "Usage: %s [ {option} | {image} ... ] {output_image}\n",name);
816 (void) FormatLocaleFile(stdout,
817 " %s -help | -version | -usage | -list {option}\n\n",name);
818 return;
819 }
820 else if (len>=6 && LocaleCompare("script",name+len-6) == 0) {
821 /* magick-script usage */
822 (void) FormatLocaleFile(stdout,
823 "Usage: %s {filename} [ {script_args} ... ]\n",name);
824 }
825 else {
826 /* magick usage */
827 (void) FormatLocaleFile(stdout,
828 "Usage: %s tool [ {option} | {image} ... ] {output_image}\n",name);
829 (void) FormatLocaleFile(stdout,
830 "Usage: %s [ {option} | {image} ... ] {output_image}\n",name);
831 (void) FormatLocaleFile(stdout,
832 " %s [ {option} | {image} ... ] -script {filename} [ {script_args} ...]\n",
833 name);
834 }
835 (void) FormatLocaleFile(stdout,
836 " %s -help | -version | -usage | -list {option}\n\n",name);
837
838 if (verbose == MagickFalse)
839 return;
840
841 (void) FormatLocaleFile(stdout,"%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
842 "All options are performed in a strict 'as you see them' order\n",
843 "You must read-in images before you can operate on them.\n",
844 "\n",
845 "Magick Script files can use any of the following forms...\n",
846 " #!/path/to/magick -script\n",
847 "or\n",
848 " #!/bin/sh\n",
849 " :; exec magick -script \"$0\" \"$@\"; exit 10\n",
850 " # Magick script from here...\n",
851 "or\n",
852 " #!/usr/bin/env magick-script\n",
853 "The latter two forms do not require the path to the command hard coded.\n",
854 "Note: \"magick-script\" needs to be linked to the \"magick\" command.\n",
855 "\n",
856 "For more information on usage, options, examples, and techniques\n",
857 "see the ImageMagick website at ", MagickAuthoritativeURL);
858
859 return;
860}
861
862/*
863 Concatenate given file arguments to the given output argument.
864 Used for a special -concatenate option used for specific 'delegates'.
865 The option is not formally documented.
866
867 magick -concatenate files... output
868
869 This is much like the UNIX "cat" command, but for both UNIX and Windows,
870 however the last argument provides the output filename.
871*/
872static MagickBooleanType ConcatenateImages(int argc,char **argv,
873 ExceptionInfo *exception )
874{
875 FILE
876 *input,
877 *output;
878
879 MagickBooleanType
880 status;
881
882 int
883 c;
884
885 ssize_t
886 i;
887
888 if (ExpandFilenames(&argc,&argv) == MagickFalse)
889 ThrowFileException(exception,ResourceLimitError,"MemoryAllocationFailed",
890 GetExceptionMessage(errno));
891 output=fopen_utf8(argv[argc-1],"wb");
892 if (output == (FILE *) NULL)
893 {
894 ThrowFileException(exception,FileOpenError,"UnableToOpenFile",
895 argv[argc-1]);
896 return(MagickFalse);
897 }
898 status=MagickTrue;
899 for (i=2; i < (ssize_t) (argc-1); i++)
900 {
901 input=fopen_utf8(argv[i],"rb");
902 if (input == (FILE *) NULL)
903 {
904 ThrowFileException(exception,FileOpenError,"UnableToOpenFile",argv[i]);
905 continue;
906 }
907 for (c=fgetc(input); c != EOF; c=fgetc(input))
908 if (fputc((char) c,output) != c)
909 status=MagickFalse;
910 (void) fclose(input);
911 (void) remove_utf8(argv[i]);
912 }
913 (void) fclose(output);
914 return(status);
915}
916
917WandExport MagickBooleanType MagickImageCommand(ImageInfo *image_info,int argc,
918 char **argv,char **metadata,ExceptionInfo *exception)
919{
921 *cli_wand;
922
923 size_t
924 len;
925
926 assert(image_info != (ImageInfo *) NULL);
927
928 /* For specific OS command line requirements */
929 ReadCommandlLine(argc,&argv);
930
931 /* Initialize special "CLI Wand" to hold images and settings (empty) */
932 cli_wand=AcquireMagickCLI(image_info,exception);
933 cli_wand->location="Initializing";
934 cli_wand->filename=argv[0];
935 cli_wand->line=1;
936
937 if (cli_wand->wand.debug != MagickFalse)
938 (void) CLILogEvent(cli_wand,CommandEvent,GetMagickModule(),
939 "\"%s\"",argv[0]);
940
941
942 GetPathComponent(argv[0],TailPath,cli_wand->wand.name);
943 SetClientName(cli_wand->wand.name);
944 ConcatenateMagickString(cli_wand->wand.name,"-CLI",MagickPathExtent);
945
946 len=strlen(argv[0]); /* precaution */
947
948 /* "convert" command - give a "deprecated" warning" */
949 if (len>=7 && LocaleCompare("convert",argv[0]+len-7) == 0) {
950 cli_wand->process_flags = ConvertCommandOptionFlags;
951 (void) FormatLocaleFile(stderr,"WARNING: %s\n",
952 "The convert command is deprecated in IMv7, use \"magick\"\n");
953 }
954
955 /* Special Case: If command name ends with "script" implied "-script" */
956 if (len>=6 && LocaleCompare("script",argv[0]+len-6) == 0) {
957 if (argc >= 2 && ( (*(argv[1]) != '-') || (strlen(argv[1]) == 1) )) {
958 GetPathComponent(argv[1],TailPath,cli_wand->wand.name);
959 ProcessScriptOptions(cli_wand,argv[1],argc,argv,2);
960 goto Magick_Command_Cleanup;
961 }
962 }
963
964 /* Special Case: Version Information and Abort */
965 if (argc == 2) {
966 if ((LocaleCompare("-version",argv[1]) == 0) || /* GNU standard option */
967 (LocaleCompare("--version",argv[1]) == 0) ) { /* just version */
968 CLIOption(cli_wand, "-version");
969 goto Magick_Command_Exit;
970 }
971 if ((LocaleCompare("-help",argv[1]) == 0) || /* GNU standard option */
972 (LocaleCompare("--help",argv[1]) == 0) ) { /* just a brief summary */
973 if (cli_wand->wand.debug != MagickFalse)
974 (void) CLILogEvent(cli_wand,CommandEvent,GetMagickModule(),
975 "- Special Option \"%s\"", argv[1]);
976 MagickUsage(MagickFalse);
977 goto Magick_Command_Exit;
978 }
979 if (LocaleCompare("-usage",argv[1]) == 0) { /* both version & usage */
980 if (cli_wand->wand.debug != MagickFalse)
981 (void) CLILogEvent(cli_wand,CommandEvent,GetMagickModule(),
982 "- Special Option \"%s\"", argv[1]);
983 CLIOption(cli_wand, "-version" );
984 MagickUsage(MagickTrue);
985 goto Magick_Command_Exit;
986 }
987 }
988
989 /* not enough arguments -- including -help */
990 if (argc < 3) {
991 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
992 "InvalidArgument","%s",argc > 1 ? argv[argc-1] : "");
993 MagickUsage(MagickFalse);
994 goto Magick_Command_Exit;
995 }
996
997 /* Special "concatenate option (hidden) for delegate usage */
998 if (LocaleCompare("-concatenate",argv[1]) == 0) {
999 if (cli_wand->wand.debug != MagickFalse)
1000 (void) CLILogEvent(cli_wand,CommandEvent,GetMagickModule(),
1001 "- Special Option \"%s\"", argv[1]);
1002 ConcatenateImages(argc,argv,exception);
1003 goto Magick_Command_Exit;
1004 }
1005
1006 /* List Information and Abort */
1007 if (argc == 3 && LocaleCompare("-list",argv[1]) == 0) {
1008 CLIOption(cli_wand, argv[1], argv[2]);
1009 goto Magick_Command_Exit;
1010 }
1011
1012 /* ------------- */
1013 /* The Main Call */
1014
1015 if (LocaleCompare("-script",argv[1]) == 0) {
1016 /* Start processing directly from script, no pre-script options
1017 Replace wand command name with script name
1018 First argument in the argv array is the script name to read.
1019 */
1020 GetPathComponent(argv[2],TailPath,cli_wand->wand.name);
1021 ProcessScriptOptions(cli_wand,argv[2],argc,argv,3);
1022 }
1023 else {
1024 /* Normal Command Line, assumes output file as last option */
1025 ProcessCommandOptions(cli_wand,argc,argv,1);
1026 }
1027 /* ------------- */
1028
1029Magick_Command_Cleanup:
1030 cli_wand->location="Cleanup";
1031 cli_wand->filename=argv[0];
1032 if (cli_wand->wand.debug != MagickFalse)
1033 (void) CLILogEvent(cli_wand,CommandEvent,GetMagickModule(),
1034 "\"%s\"",argv[0]);
1035
1036 /* recover original image_info and clean up stacks
1037 FUTURE: "-reset stacks" option */
1038 while ((cli_wand->image_list_stack != (CLIStack *) NULL) &&
1039 (cli_wand->image_list_stack->next != (CLIStack *) NULL))
1040 CLIOption(cli_wand,")");
1041 while ((cli_wand->image_info_stack != (CLIStack *) NULL) &&
1042 (cli_wand->image_info_stack->next != (CLIStack *) NULL))
1043 CLIOption(cli_wand,"}");
1044
1045 /* assert we have recovered the original structures */
1046 assert(cli_wand->wand.image_info == image_info);
1047 assert(cli_wand->wand.exception == exception);
1048
1049 /* Handle metadata for ImageMagickObject COM object for Windows VBS */
1050 if ((cli_wand->wand.images != (Image *) NULL) &&
1051 (metadata != (char **) NULL))
1052 {
1053 const char
1054 *format;
1055
1056 char
1057 *text;
1058
1059 format="%w,%h,%m"; /* Get this from image_info Option splaytree */
1060 text=InterpretImageProperties(image_info,cli_wand->wand.images,format,
1061 exception);
1062 if (text == (char *) NULL)
1063 ThrowMagickException(exception,GetMagickModule(),ResourceLimitError,
1064 "MemoryAllocationFailed","`%s'", GetExceptionMessage(errno));
1065 else
1066 {
1067 (void) ConcatenateString(&(*metadata),text);
1068 text=DestroyString(text);
1069 }
1070 }
1071
1072Magick_Command_Exit:
1073 cli_wand->location="Exiting";
1074 cli_wand->filename=argv[0];
1075 if (cli_wand->wand.debug != MagickFalse)
1076 (void) CLILogEvent(cli_wand,CommandEvent,GetMagickModule(),
1077 "\"%s\"",argv[0]);
1078
1079 /* Destroy the special CLI Wand */
1080 cli_wand->wand.image_info = (ImageInfo *) NULL; /* not these */
1081 cli_wand->wand.exception = (ExceptionInfo *) NULL;
1082 cli_wand=DestroyMagickCLI(cli_wand);
1083
1084 return(exception->severity < ErrorException ? MagickTrue : MagickFalse);
1085}