MagickWand  7.1.2-29
Convert, Edit, Or Compose Bitmap Images
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 */
117 WandExport 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[%.17g]: %.17gi %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 */
341 WandExport 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;
385 DisableMSCWarning(4127)
386  while (1) {
387 RestoreMSCWarning
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 
484 DisableMSCWarning(4127)
485  } while (0); /* break block to next option */
486 RestoreMSCWarning
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  */
499 loop_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 */
588 WandExport 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 
709 DisableMSCWarning(4127)
710  } while (0); /* break block to next option */
711 RestoreMSCWarning
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 
802 static 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 
1166 static 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 */
1240 static 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 
1289 WandExport 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  GetPathComponent(argv[0],TailPath,cli_wand->wand.name);
1314  (void) SetClientName(cli_wand->wand.name);
1315  (void) ConcatenateMagickString(cli_wand->wand.name,"-CLI",MagickPathExtent);
1316 
1317  len=strlen(argv[0]); /* precaution */
1318 
1319  /* "convert" command - give a "deprecated" warning" */
1320  if (len>=7 && LocaleCompare("convert",argv[0]+len-7) == 0) {
1321  cli_wand->process_flags = ConvertCommandOptionFlags;
1322  (void) FormatLocaleFile(stderr,"WARNING: %s\n",
1323  "The convert command is deprecated in IMv7, use \"magick\"\n");
1324  }
1325 
1326  /* Special Case: If command name ends with "script" implied "-script" */
1327  if (len>=6 && LocaleCompare("script",argv[0]+len-6) == 0) {
1328  if (argc >= 2 && ( (*(argv[1]) != '-') || (strlen(argv[1]) == 1) )) {
1329  GetPathComponent(argv[1],TailPath,cli_wand->wand.name);
1330  ProcessScriptOptions(cli_wand,argv[1],argc,argv,2);
1331  goto Magick_Command_Cleanup;
1332  }
1333  }
1334 
1335  /* Special Case: Version Information and Abort */
1336  if (argc == 2) {
1337  if ((LocaleCompare("-version",argv[1]) == 0) || /* GNU standard option */
1338  (LocaleCompare("--version",argv[1]) == 0) ) { /* just version */
1339  CLIOption(cli_wand, "-version");
1340  goto Magick_Command_Exit;
1341  }
1342  if ((LocaleCompare("-help",argv[1]) == 0) || /* GNU standard option */
1343  (LocaleCompare("--help",argv[1]) == 0) ) { /* just a brief summary */
1344  if (cli_wand->wand.debug != MagickFalse)
1345  (void) CLILogEvent(cli_wand,CommandEvent,GetMagickModule(),
1346  "- Special Option \"%s\"", argv[1]);
1347  MagickUsage(MagickFalse);
1348  goto Magick_Command_Exit;
1349  }
1350  if (LocaleCompare("-usage",argv[1]) == 0) { /* both version & usage */
1351  if (cli_wand->wand.debug != MagickFalse)
1352  (void) CLILogEvent(cli_wand,CommandEvent,GetMagickModule(),
1353  "- Special Option \"%s\"", argv[1]);
1354  CLIOption(cli_wand, "-version" );
1355  MagickUsage(MagickTrue);
1356  goto Magick_Command_Exit;
1357  }
1358  }
1359 
1360  /* not enough arguments -- including -help */
1361  if (argc < 3) {
1362  (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1363  "InvalidArgument","%s",argc > 1 ? argv[argc-1] : "");
1364  MagickUsage(MagickFalse);
1365  goto Magick_Command_Exit;
1366  }
1367 
1368  /* Special "concatenate option (hidden) for delegate usage */
1369  if (LocaleCompare("-concatenate",argv[1]) == 0) {
1370  if (cli_wand->wand.debug != MagickFalse)
1371  (void) CLILogEvent(cli_wand,CommandEvent,GetMagickModule(),
1372  "- Special Option \"%s\"", argv[1]);
1373  (void) ConcatenateImages(argc,argv,exception);
1374  goto Magick_Command_Exit;
1375  }
1376 
1377  /* List Information and Abort */
1378  if (argc == 3 && LocaleCompare("-list",argv[1]) == 0) {
1379  CLIOption(cli_wand, argv[1], argv[2]);
1380  goto Magick_Command_Exit;
1381  }
1382 
1383  /* ------------- */
1384  /* The Main Call */
1385 
1386  if (LocaleCompare("-script",argv[1]) == 0) {
1387  /* Start processing directly from script, no pre-script options
1388  Replace wand command name with script name
1389  First argument in the argv array is the script name to read.
1390  */
1391  GetPathComponent(argv[2],TailPath,cli_wand->wand.name);
1392  ProcessScriptOptions(cli_wand,argv[2],argc,argv,3);
1393  }
1394  else {
1395  /* Normal Command Line, assumes output file as last option */
1396  ProcessCommandOptions(cli_wand,argc,argv,1);
1397  }
1398  /* ------------- */
1399 
1400 Magick_Command_Cleanup:
1401  cli_wand->location="Cleanup";
1402  cli_wand->filename=argv[0];
1403  if (cli_wand->wand.debug != MagickFalse)
1404  (void) CLILogEvent(cli_wand,CommandEvent,GetMagickModule(),
1405  "\"%s\"",argv[0]);
1406 
1407  /* recover original image_info and clean up stacks
1408  FUTURE: "-reset stacks" option */
1409  while ((cli_wand->image_list_stack != (CLIStack *) NULL) &&
1410  (cli_wand->image_list_stack->next != (CLIStack *) NULL))
1411  CLIOption(cli_wand,")");
1412  while ((cli_wand->image_info_stack != (CLIStack *) NULL) &&
1413  (cli_wand->image_info_stack->next != (CLIStack *) NULL))
1414  CLIOption(cli_wand,"}");
1415 
1416 Magick_Command_Exit:
1417  if ((cli_wand->wand.image_info != image_info) ||
1418  (cli_wand->wand.exception != exception))
1419  {
1420  CLIStack
1421  *node;
1422 
1423  /*
1424  Pop image_info settings from stack.
1425  */
1426  node=(CLIStack *) cli_wand->image_info_stack;
1427  cli_wand->image_info_stack=node->next;
1428  (void) DestroyImageInfo(cli_wand->wand.image_info);
1429  cli_wand->wand.image_info=(ImageInfo *) node->data;
1430  node=(CLIStack *) RelinquishMagickMemory(node);
1431  }
1432 
1433  /* Handle metadata for ImageMagickObject COM object for Windows VBS */
1434  if ((cli_wand->wand.images != (Image *) NULL) && (metadata != (char **) NULL))
1435  {
1436  const char
1437  *format;
1438 
1439  char
1440  *text;
1441 
1442  format="%w,%h,%m"; /* Get this from image_info Option splaytree */
1443  text=InterpretImageProperties(image_info,cli_wand->wand.images,format,
1444  exception);
1445  if (text == (char *) NULL)
1446  {
1447  char *message = GetExceptionMessage(errno);
1448  (void) ThrowMagickException(exception,GetMagickModule(),
1449  ResourceLimitError,"MemoryAllocationFailed","`%s'",message);
1450  message=DestroyString(message);
1451  }
1452  else
1453  {
1454  (void) ConcatenateString(&(*metadata),text);
1455  text=DestroyString(text);
1456  }
1457  }
1458 
1459  cli_wand->location="Exiting";
1460  cli_wand->filename=argv[0];
1461  if (cli_wand->wand.debug != MagickFalse)
1462  (void) CLILogEvent(cli_wand,CommandEvent,GetMagickModule(),
1463  "\"%s\"",argv[0]);
1464 
1465  /* FIX: free ImageInfo cloned inside DrawInfo by AcquireMagickCLI/GetDrawInfo */
1466  if (cli_wand->draw_info != (DrawInfo *) NULL &&
1467  cli_wand->draw_info->image_info != (ImageInfo *) NULL)
1468  cli_wand->draw_info->image_info =
1469  DestroyImageInfo(cli_wand->draw_info->image_info);
1470 
1471  /* optional: now free the DrawInfo itself */
1472  if (cli_wand->draw_info != (DrawInfo *) NULL)
1473  cli_wand->draw_info = DestroyDrawInfo(cli_wand->draw_info);
1474 
1475  /* Destroy the special CLI Wand */
1476  cli_wand->wand.image_info = (ImageInfo *) NULL; /* not these */
1477  cli_wand->wand.exception = (ExceptionInfo *) NULL;
1478  cli_wand=DestroyMagickCLI(cli_wand);
1479 
1480  return(exception->severity < ErrorException ? MagickTrue : MagickFalse);
1481 }