MagickWand  7.1.1-43
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/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 */
116 WandExport 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 */
340 WandExport 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;
384 DisableMSCWarning(4127)
385  while (1) {
386 RestoreMSCWarning
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 
483 DisableMSCWarning(4127)
484  } while (0); /* break block to next option */
485 RestoreMSCWarning
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  */
498 loop_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 */
587 WandExport 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 
708 DisableMSCWarning(4127)
709  } while (0); /* break block to next option */
710 RestoreMSCWarning
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 
801 static MagickBooleanType MagickCommandUsage(void)
802 {
803  static const char
804  channel_operators[] =
805  " -channel-fx expression\n"
806  " exchange, extract, or transfer one or more image channels\n"
807  " -separate separate an image channel into a grayscale image",
808  miscellaneous[] =
809  " -debug events display copious debugging information\n"
810  " -distribute-cache port\n"
811  " distributed pixel cache spanning one or more servers\n"
812  " -help print program options\n"
813  " -list type print a list of supported option arguments\n"
814  " -log format format of debugging information\n"
815  " -usage print program usage\n"
816  " -version print version information",
817  operators[] =
818  " -adaptive-blur geometry\n"
819  " adaptively blur pixels; decrease effect near edges\n"
820  " -adaptive-resize geometry\n"
821  " adaptively resize image using 'mesh' interpolation\n"
822  " -adaptive-sharpen geometry\n"
823  " adaptively sharpen pixels; increase effect near edges\n"
824  " -alpha option on, activate, off, deactivate, set, opaque, copy\n"
825  " transparent, extract, background, or shape\n"
826  " -annotate geometry text\n"
827  " annotate the image with text\n"
828  " -auto-gamma automagically adjust gamma level of image\n"
829  " -auto-level automagically adjust color levels of image\n"
830  " -auto-orient automagically orient (rotate) image\n"
831  " -auto-threshold method\n"
832  " automatically perform image thresholding\n"
833  " -bench iterations measure performance\n"
834  " -bilateral-blur geometry\n"
835  " non-linear, edge-preserving, and noise-reducing smoothing filter\n"
836  " -black-threshold value\n"
837  " force all pixels below the threshold into black\n"
838  " -blue-shift factor simulate a scene at nighttime in the moonlight\n"
839  " -blur geometry reduce image noise and reduce detail levels\n"
840  " -border geometry surround image with a border of color\n"
841  " -bordercolor color border color\n"
842  " -brightness-contrast geometry\n"
843  " improve brightness / contrast of the image\n"
844  " -canny geometry detect edges in the image\n"
845  " -cdl filename color correct with a color decision list\n"
846  " -channel mask set the image channel mask\n"
847  " -charcoal radius simulate a charcoal drawing\n"
848  " -chop geometry remove pixels from the image interior\n"
849  " -clahe geometry contrast limited adaptive histogram equalization\n"
850  " -clamp keep pixel values in range (0-QuantumRange)\n"
851  " -colorize value colorize the image with the fill color\n"
852  " -color-matrix matrix apply color correction to the image\n"
853  " -colors value preferred number of colors in the image\n"
854  " -connected-components connectivity\n"
855  " connected-components uniquely labeled\n"
856  " -contrast enhance or reduce the image contrast\n"
857  " -contrast-stretch geometry\n"
858  " improve contrast by 'stretching' the intensity range\n"
859  " -convolve coefficients\n"
860  " apply a convolution kernel to the image\n"
861  " -cycle amount cycle the image colormap\n"
862  " -decipher filename convert cipher pixels to plain pixels\n"
863  " -deskew threshold straighten an image\n"
864  " -despeckle reduce the speckles within an image\n"
865  " -distort method args\n"
866  " distort images according to given method and args\n"
867  " -draw string annotate the image with a graphic primitive\n"
868  " -edge radius apply a filter to detect edges in the image\n"
869  " -encipher filename convert plain pixels to cipher pixels\n"
870  " -emboss radius emboss an image\n"
871  " -enhance apply a digital filter to enhance a noisy image\n"
872  " -equalize perform histogram equalization to an image\n"
873  " -evaluate operator value\n"
874  " evaluate an arithmetic, relational, or logical expression\n"
875  " -extent geometry set the image size\n"
876  " -extract geometry extract area from image\n"
877  " -fft implements the discrete Fourier transform (DFT)\n"
878  " -flip flip image vertically\n"
879  " -floodfill geometry color\n"
880  " floodfill the image with color\n"
881  " -flop flop image horizontally\n"
882  " -frame geometry surround image with an ornamental border\n"
883  " -function name parameters\n"
884  " apply function over image values\n"
885  " -gamma value level of gamma correction\n"
886  " -gaussian-blur geometry\n"
887  " reduce image noise and reduce detail levels\n"
888  " -geometry geometry preferred size or location of the image\n"
889  " -grayscale method convert image to grayscale\n"
890  " -hough-lines geometry\n"
891  " identify lines in the image\n"
892  " -identify identify the format and characteristics of the image\n"
893  " -ift implements the inverse discrete Fourier transform (DFT)\n"
894  " -implode amount implode image pixels about the center\n"
895  " -integral calculate the sum of values (pixel values) in the image\n"
896  " -interpolative-resize geometry\n"
897  " resize image using interpolation\n"
898  " -kmeans geometry K means color reduction\n"
899  " -kuwahara geometry edge preserving noise reduction filter\n"
900  " -lat geometry local adaptive thresholding\n"
901  " -level value adjust the level of image contrast\n"
902  " -level-colors color,color\n"
903  " level image with the given colors\n"
904  " -linear-stretch geometry\n"
905  " improve contrast by 'stretching with saturation'\n"
906  " -liquid-rescale geometry\n"
907  " rescale image with seam-carving\n"
908  " -local-contrast geometry\n"
909  " enhance local contrast\n"
910  " -mean-shift geometry delineate arbitrarily shaped clusters in the image\n"
911  " -median geometry apply a median filter to the image\n"
912  " -mode geometry make each pixel the 'predominant color' of the\n"
913  " neighborhood\n"
914  " -modulate value vary the brightness, saturation, and hue\n"
915  " -monochrome transform image to black and white\n"
916  " -morphology method kernel\n"
917  " apply a morphology method to the image\n"
918  " -motion-blur geometry\n"
919  " simulate motion blur\n"
920  " -negate replace every pixel with its complementary color \n"
921  " -noise geometry add or reduce noise in an image\n"
922  " -normalize transform image to span the full range of colors\n"
923  " -opaque color change this color to the fill color\n"
924  " -ordered-dither NxN\n"
925  " add a noise pattern to the image with specific\n"
926  " amplitudes\n"
927  " -paint radius simulate an oil painting\n"
928  " -perceptible epsilon\n"
929  " pixel value less than |epsilon| become epsilon or\n"
930  " -epsilon\n"
931  " -polaroid angle simulate a Polaroid picture\n"
932  " -posterize levels reduce the image to a limited number of color levels\n"
933  " -profile filename add, delete, or apply an image profile\n"
934  " -quantize colorspace reduce colors in this colorspace\n"
935  " -raise value lighten/darken image edges to create a 3-D effect\n"
936  " -random-threshold low,high\n"
937  " random threshold the image\n"
938  " -range-threshold values\n"
939  " perform either hard or soft thresholding within some range of values in an image\n"
940  " -region geometry apply options to a portion of the image\n"
941  " -render render vector graphics\n"
942  " -resample geometry change the resolution of an image\n"
943  " -reshape geometry reshape the image\n"
944  " -resize geometry resize the image\n"
945  " -roll geometry roll an image vertically or horizontally\n"
946  " -rotate degrees apply Paeth rotation to the image\n"
947  " -rotational-blur angle\n"
948  " rotational blur the image\n"
949  " -sample geometry scale image with pixel sampling\n"
950  " -scale geometry scale the image\n"
951  " -segment values segment an image\n"
952  " -selective-blur geometry\n"
953  " selectively blur pixels within a contrast threshold\n"
954  " -sepia-tone threshold\n"
955  " simulate a sepia-toned photo\n"
956  " -set property value set an image property\n"
957  " -shade degrees shade the image using a distant light source\n"
958  " -shadow geometry simulate an image shadow\n"
959  " -sharpen geometry sharpen the image\n"
960  " -shave geometry shave pixels from the image edges\n"
961  " -shear geometry slide one edge of the image along the X or Y axis\n"
962  " -sigmoidal-contrast geometry\n"
963  " increase the contrast without saturating highlights or\n"
964  " shadows\n"
965  " -sketch geometry simulate a pencil sketch\n"
966  " -solarize threshold negate all pixels above the threshold level\n"
967  " -sort-pixels sort each scanline in ascending order of intensity\n"
968  " -sparse-color method args\n"
969  " fill in a image based on a few color points\n"
970  " -splice geometry splice the background color into the image\n"
971  " -spread radius displace image pixels by a random amount\n"
972  " -statistic type geometry\n"
973  " replace each pixel with corresponding statistic from the\n"
974  " neighborhood\n"
975  " -strip strip image of all profiles and comments\n"
976  " -swirl degrees swirl image pixels about the center\n"
977  " -threshold value threshold the image\n"
978  " -thumbnail geometry create a thumbnail of the image\n"
979  " -tile filename tile image when filling a graphic primitive\n"
980  " -tint value tint the image with the fill color\n"
981  " -transform affine transform image\n"
982  " -transparent color make this color transparent within the image\n"
983  " -transpose flip image vertically and rotate 90 degrees\n"
984  " -transverse flop image horizontally and rotate 270 degrees\n"
985  " -trim trim image edges\n"
986  " -type type image type\n"
987  " -unique-colors discard all but one of any pixel color\n"
988  " -unsharp geometry sharpen the image\n"
989  " -vignette geometry soften the edges of the image in vignette style\n"
990  " -wave geometry alter an image along a sine wave\n"
991  " -wavelet-denoise threshold\n"
992  " removes noise from the image using a wavelet transform\n"
993  " -white-balance automagically adjust white balance of image\n"
994  " -white-threshold value\n"
995  " force all pixels above the threshold into white",
996  sequence_operators[] =
997  " -append append an image sequence\n"
998  " -clut apply a color lookup table to the image\n"
999  " -coalesce merge a sequence of images\n"
1000  " -combine combine a sequence of images\n"
1001  " -compare mathematically and visually annotate the difference between an image and its reconstruction\n"
1002  " -complex operator perform complex mathematics on an image sequence\n"
1003  " -composite composite image\n"
1004  " -copy geometry offset\n"
1005  " copy pixels from one area of an image to another\n"
1006  " -crop geometry cut out a rectangular region of the image\n"
1007  " -deconstruct break down an image sequence into constituent parts\n"
1008  " -evaluate-sequence operator\n"
1009  " evaluate an arithmetic, relational, or logical expression\n"
1010  " -flatten flatten a sequence of images\n"
1011  " -fx expression apply mathematical expression to an image channel(s)\n"
1012  " -hald-clut apply a Hald color lookup table to the image\n"
1013  " -layers method optimize, merge, or compare image layers\n"
1014  " -morph value morph an image sequence\n"
1015  " -mosaic create a mosaic from an image sequence\n"
1016  " -poly terms build a polynomial from the image sequence and the corresponding\n"
1017  " terms (coefficients and degree pairs).\n"
1018  " -print string interpret string and print to console\n"
1019  " -process arguments process the image with a custom image filter\n"
1020  " -smush geometry smush an image sequence together\n"
1021  " -write filename write images to this file",
1022  settings[] =
1023  " -adjoin join images into a single multi-image file\n"
1024  " -affine matrix affine transform matrix\n"
1025  " -alpha option activate, deactivate, reset, or set the alpha channel\n"
1026  " -antialias remove pixel-aliasing\n"
1027  " -authenticate password\n"
1028  " decipher image with this password\n"
1029  " -attenuate value lessen (or intensify) when adding noise to an image\n"
1030  " -background color background color\n"
1031  " -bias value add bias when convolving an image\n"
1032  " -black-point-compensation\n"
1033  " use black point compensation\n"
1034  " -blue-primary point chromaticity blue primary point\n"
1035  " -bordercolor color border color\n"
1036  " -caption string assign a caption to an image\n"
1037  " -clip clip along the first path from the 8BIM profile\n"
1038  " -clip-mask filename associate a clip mask with the image\n"
1039  " -clip-path id clip along a named path from the 8BIM profile\n"
1040  " -colorspace type alternate image colorspace\n"
1041  " -comment string annotate image with comment\n"
1042  " -compose operator set image composite operator\n"
1043  " -compress type type of pixel compression when writing the image\n"
1044  " -define format:option\n"
1045  " define one or more image format options\n"
1046  " -delay value display the next image after pausing\n"
1047  " -density geometry horizontal and vertical density of the image\n"
1048  " -depth value image depth\n"
1049  " -direction type render text right-to-left or left-to-right\n"
1050  " -display server get image or font from this X server\n"
1051  " -dispose method layer disposal method\n"
1052  " -dither method apply error diffusion to image\n"
1053  " -encoding type text encoding type\n"
1054  " -endian type endianness (MSB or LSB) of the image\n"
1055  " -family name render text with this font family\n"
1056  " -features distance analyze image features (e.g. contrast, correlation)\n"
1057  " -fill color color to use when filling a graphic primitive\n"
1058  " -filter type use this filter when resizing an image\n"
1059  " -font name render text with this font\n"
1060  " -format \"string\" output formatted image characteristics\n"
1061  " -fuzz distance colors within this distance are considered equal\n"
1062  " -gravity type horizontal and vertical text placement\n"
1063  " -green-primary point chromaticity green primary point\n"
1064  " -illuminant type reference illuminant\n"
1065  " -intensity method method to generate an intensity value from a pixel\n"
1066  " -intent type type of rendering intent when managing the image color\n"
1067  " -interlace type type of image interlacing scheme\n"
1068  " -interline-spacing value\n"
1069  " set the space between two text lines\n"
1070  " -interpolate method pixel color interpolation method\n"
1071  " -interword-spacing value\n"
1072  " set the space between two words\n"
1073  " -kerning value set the space between two letters\n"
1074  " -label string assign a label to an image\n"
1075  " -limit type value pixel cache resource limit\n"
1076  " -loop iterations add Netscape loop extension to your GIF animation\n"
1077  " -matte store matte channel if the image has one\n"
1078  " -mattecolor color frame color\n"
1079  " -moments report image moments\n"
1080  " -monitor monitor progress\n"
1081  " -orient type image orientation\n"
1082  " -page geometry size and location of an image canvas (setting)\n"
1083  " -ping efficiently determine image attributes\n"
1084  " -pointsize value font point size\n"
1085  " -precision value maximum number of significant digits to print\n"
1086  " -preview type image preview type\n"
1087  " -quality value JPEG/MIFF/PNG compression level\n"
1088  " -quiet suppress all warning messages\n"
1089  " -read-mask filename associate a read mask with the image\n"
1090  " -red-primary point chromaticity red primary point\n"
1091  " -regard-warnings pay attention to warning messages\n"
1092  " -remap filename transform image colors to match this set of colors\n"
1093  " -repage geometry size and location of an image canvas\n"
1094  " -respect-parentheses settings remain in effect until parenthesis boundary\n"
1095  " -sampling-factor geometry\n"
1096  " horizontal and vertical sampling factor\n"
1097  " -scene value image scene number\n"
1098  " -seed value seed a new sequence of pseudo-random numbers\n"
1099  " -size geometry width and height of image\n"
1100  " -stretch type render text with this font stretch\n"
1101  " -stroke color graphic primitive stroke color\n"
1102  " -strokewidth value graphic primitive stroke width\n"
1103  " -style type render text with this font style\n"
1104  " -support factor resize support: > 1.0 is blurry, < 1.0 is sharp\n"
1105  " -synchronize synchronize image to storage device\n"
1106  " -taint declare the image as modified\n"
1107  " -texture filename name of texture to tile onto the image background\n"
1108  " -tile-offset geometry\n"
1109  " tile offset\n"
1110  " -treedepth value color tree depth\n"
1111  " -transparent-color color\n"
1112  " transparent color\n"
1113  " -undercolor color annotation bounding box color\n"
1114  " -units type the units of image resolution\n"
1115  " -verbose print detailed information about the image\n"
1116  " -view FlashPix viewing transforms\n"
1117  " -virtual-pixel method\n"
1118  " virtual pixel access method\n"
1119  " -weight type render text with this font weight\n"
1120  " -white-point point chromaticity white point\n"
1121  " -write-mask filename associate a write mask with the image"
1122  " -word-break type sets whether line breaks appear wherever the text would otherwise overflow",
1123  stack_operators[] =
1124  " -clone indexes clone an image\n"
1125  " -delete indexes delete the image from the image sequence\n"
1126  " -duplicate count,indexes\n"
1127  " duplicate an image one or more times\n"
1128  " -insert index insert last image into the image sequence\n"
1129  " -reverse reverse image sequence\n"
1130  " -swap indexes swap two images in the image sequence";
1131 
1132  ListMagickVersion(stdout);
1133  (void) FormatLocaleFile(stdout,
1134  "Usage: %s tool [ {option} | {image} ... ] {output_image}\n",
1135  GetClientName());
1136  (void) FormatLocaleFile(stdout,
1137  "Usage: %s [ {option} | {image} ... ] {output_image}\n",GetClientName());
1138  (void) FormatLocaleFile(stdout,
1139  " %s [ {option} | {image} ... ] -script {filename} [ {script_args} ...]\n",
1140  GetClientName());
1141  (void) FormatLocaleFile(stdout,"\nImage Settings:\n");
1142  (void) FormatLocaleFile(stdout,"%s\n",settings);
1143  (void) FormatLocaleFile(stdout,"\nImage Operators:\n");
1144  (void) FormatLocaleFile(stdout,"%s\n",operators);
1145  (void) FormatLocaleFile(stdout,"\nImage Channel Operators:\n");
1146  (void) FormatLocaleFile(stdout,"%s\n",channel_operators);
1147  (void) FormatLocaleFile(stdout,"\nImage Sequence Operators:\n");
1148  (void) FormatLocaleFile(stdout,"%s\n",sequence_operators);
1149  (void) FormatLocaleFile(stdout,"\nImage Stack Operators:\n");
1150  (void) FormatLocaleFile(stdout,"%s\n",stack_operators);
1151  (void) FormatLocaleFile(stdout,"\nMiscellaneous Options:\n");
1152  (void) FormatLocaleFile(stdout,"%s\n",miscellaneous);
1153  (void) FormatLocaleFile(stdout,
1154  "\nBy default, the image format of 'file' is determined by its magic\n");
1155  (void) FormatLocaleFile(stdout,
1156  "number. To specify a particular image format, precede the filename\n");
1157  (void) FormatLocaleFile(stdout,
1158  "with an image format name and a colon (i.e. ps:image) or specify the\n");
1159  (void) FormatLocaleFile(stdout,
1160  "image type as the filename suffix (i.e. image.ps). Specify 'file' as\n");
1161  (void) FormatLocaleFile(stdout,"'-' for standard input or output.\n");
1162  return(MagickTrue);
1163 }
1164 
1165 static void MagickUsage(MagickBooleanType verbose)
1166 {
1167  const char
1168  *name;
1169 
1170  size_t
1171  len;
1172 
1173  name=GetClientName();
1174  len=strlen(name);
1175 
1176  if (verbose == MagickFalse)
1177  {
1178  MagickCommandUsage();
1179  return;
1180  }
1181 
1182  if (len>=7 && LocaleCompare("convert",name+len-7) == 0) {
1183  /* convert usage */
1184  (void) FormatLocaleFile(stdout,
1185  "Usage: %s [ {option} | {image} ... ] {output_image}\n",name);
1186  (void) FormatLocaleFile(stdout,
1187  " %s -help | -version | -usage | -list {option}\n\n",name);
1188  return;
1189  }
1190  else if (len>=6 && LocaleCompare("script",name+len-6) == 0) {
1191  /* magick-script usage */
1192  (void) FormatLocaleFile(stdout,
1193  "Usage: %s {filename} [ {script_args} ... ]\n",name);
1194  }
1195  else {
1196  /* magick usage */
1197  (void) FormatLocaleFile(stdout,
1198  "Usage: %s tool [ {option} | {image} ... ] {output_image}\n",name);
1199  (void) FormatLocaleFile(stdout,
1200  "Usage: %s [ {option} | {image} ... ] {output_image}\n",name);
1201  (void) FormatLocaleFile(stdout,
1202  " %s [ {option} | {image} ... ] -script {filename} [ {script_args} ...]\n",
1203  name);
1204  }
1205  (void) FormatLocaleFile(stdout,
1206  " %s -help | -version | -usage | -list {option}\n\n",name);
1207 
1208  (void) FormatLocaleFile(stdout,"%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
1209  "All options are performed in a strict 'as you see them' order\n",
1210  "You must read-in images before you can operate on them.\n",
1211  "\n",
1212  "Magick Script files can use any of the following forms...\n",
1213  " #!/path/to/magick -script\n",
1214  "or\n",
1215  " #!/bin/sh\n",
1216  " :; exec magick -script \"$0\" \"$@\"; exit 10\n",
1217  " # Magick script from here...\n",
1218  "or\n",
1219  " #!/usr/bin/env magick-script\n",
1220  "The latter two forms do not require the path to the command hard coded.\n",
1221  "Note: \"magick-script\" needs to be linked to the \"magick\" command.\n",
1222  "\n",
1223  "For more information on usage, options, examples, and techniques\n",
1224  "see the ImageMagick website at ", MagickAuthoritativeURL);
1225 
1226  return;
1227 }
1228 
1229 /*
1230  Concatenate given file arguments to the given output argument.
1231  Used for a special -concatenate option used for specific 'delegates'.
1232  The option is not formally documented.
1233 
1234  magick -concatenate files... output
1235 
1236  This is much like the UNIX "cat" command, but for both UNIX and Windows,
1237  however the last argument provides the output filename.
1238 */
1239 static MagickBooleanType ConcatenateImages(int argc,char **argv,
1240  ExceptionInfo *exception )
1241 {
1242  FILE
1243  *input,
1244  *output;
1245 
1246  MagickBooleanType
1247  status;
1248 
1249  int
1250  c;
1251 
1252  ssize_t
1253  i;
1254 
1255  if (ExpandFilenames(&argc,&argv) == MagickFalse)
1256  ThrowFileException(exception,ResourceLimitError,"MemoryAllocationFailed",
1257  GetExceptionMessage(errno));
1258  output=fopen_utf8(argv[argc-1],"wb");
1259  if (output == (FILE *) NULL)
1260  {
1261  ThrowFileException(exception,FileOpenError,"UnableToOpenFile",
1262  argv[argc-1]);
1263  return(MagickFalse);
1264  }
1265  status=MagickTrue;
1266  for (i=2; i < (ssize_t) (argc-1); i++)
1267  {
1268  input=fopen_utf8(argv[i],"rb");
1269  if (input == (FILE *) NULL)
1270  {
1271  ThrowFileException(exception,FileOpenError,"UnableToOpenFile",argv[i]);
1272  continue;
1273  }
1274  for (c=fgetc(input); c != EOF; c=fgetc(input))
1275  if (fputc((char) c,output) != c)
1276  status=MagickFalse;
1277  (void) fclose(input);
1278  (void) remove_utf8(argv[i]);
1279  }
1280  (void) fclose(output);
1281  return(status);
1282 }
1283 
1284 WandExport MagickBooleanType MagickImageCommand(ImageInfo *image_info,int argc,
1285  char **argv,char **metadata,ExceptionInfo *exception)
1286 {
1287  MagickCLI
1288  *cli_wand;
1289 
1290  size_t
1291  len;
1292 
1293  assert(image_info != (ImageInfo *) NULL);
1294 
1295  /* For specific OS command line requirements */
1296  ReadCommandlLine(argc,&argv);
1297 
1298  /* Initialize special "CLI Wand" to hold images and settings (empty) */
1299  cli_wand=AcquireMagickCLI(image_info,exception);
1300  cli_wand->location="Initializing";
1301  cli_wand->filename=argv[0];
1302  cli_wand->line=1;
1303 
1304  if (cli_wand->wand.debug != MagickFalse)
1305  (void) CLILogEvent(cli_wand,CommandEvent,GetMagickModule(),
1306  "\"%s\"",argv[0]);
1307 
1308 
1309  GetPathComponent(argv[0],TailPath,cli_wand->wand.name);
1310  SetClientName(cli_wand->wand.name);
1311  ConcatenateMagickString(cli_wand->wand.name,"-CLI",MagickPathExtent);
1312 
1313  len=strlen(argv[0]); /* precaution */
1314 
1315  /* "convert" command - give a "deprecated" warning" */
1316  if (len>=7 && LocaleCompare("convert",argv[0]+len-7) == 0) {
1317  cli_wand->process_flags = ConvertCommandOptionFlags;
1318  }
1319 
1320  /* Special Case: If command name ends with "script" implied "-script" */
1321  if (len>=6 && LocaleCompare("script",argv[0]+len-6) == 0) {
1322  if (argc >= 2 && ( (*(argv[1]) != '-') || (strlen(argv[1]) == 1) )) {
1323  GetPathComponent(argv[1],TailPath,cli_wand->wand.name);
1324  ProcessScriptOptions(cli_wand,argv[1],argc,argv,2);
1325  goto Magick_Command_Cleanup;
1326  }
1327  }
1328 
1329  /* Special Case: Version Information and Abort */
1330  if (argc == 2) {
1331  if ((LocaleCompare("-version",argv[1]) == 0) || /* GNU standard option */
1332  (LocaleCompare("--version",argv[1]) == 0) ) { /* just version */
1333  CLIOption(cli_wand, "-version");
1334  goto Magick_Command_Exit;
1335  }
1336  if ((LocaleCompare("-help",argv[1]) == 0) || /* GNU standard option */
1337  (LocaleCompare("--help",argv[1]) == 0) ) { /* just a brief summary */
1338  if (cli_wand->wand.debug != MagickFalse)
1339  (void) CLILogEvent(cli_wand,CommandEvent,GetMagickModule(),
1340  "- Special Option \"%s\"", argv[1]);
1341  MagickUsage(MagickFalse);
1342  goto Magick_Command_Exit;
1343  }
1344  if (LocaleCompare("-usage",argv[1]) == 0) { /* both version & usage */
1345  if (cli_wand->wand.debug != MagickFalse)
1346  (void) CLILogEvent(cli_wand,CommandEvent,GetMagickModule(),
1347  "- Special Option \"%s\"", argv[1]);
1348  CLIOption(cli_wand, "-version" );
1349  MagickUsage(MagickTrue);
1350  goto Magick_Command_Exit;
1351  }
1352  }
1353 
1354  /* not enough arguments -- including -help */
1355  if (argc < 3) {
1356  (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1357  "InvalidArgument","%s",argc > 1 ? argv[argc-1] : "");
1358  MagickUsage(MagickFalse);
1359  goto Magick_Command_Exit;
1360  }
1361 
1362  /* Special "concatenate option (hidden) for delegate usage */
1363  if (LocaleCompare("-concatenate",argv[1]) == 0) {
1364  if (cli_wand->wand.debug != MagickFalse)
1365  (void) CLILogEvent(cli_wand,CommandEvent,GetMagickModule(),
1366  "- Special Option \"%s\"", argv[1]);
1367  ConcatenateImages(argc,argv,exception);
1368  goto Magick_Command_Exit;
1369  }
1370 
1371  /* List Information and Abort */
1372  if (argc == 3 && LocaleCompare("-list",argv[1]) == 0) {
1373  CLIOption(cli_wand, argv[1], argv[2]);
1374  goto Magick_Command_Exit;
1375  }
1376 
1377  /* ------------- */
1378  /* The Main Call */
1379 
1380  if (LocaleCompare("-script",argv[1]) == 0) {
1381  /* Start processing directly from script, no pre-script options
1382  Replace wand command name with script name
1383  First argument in the argv array is the script name to read.
1384  */
1385  GetPathComponent(argv[2],TailPath,cli_wand->wand.name);
1386  ProcessScriptOptions(cli_wand,argv[2],argc,argv,3);
1387  }
1388  else {
1389  /* Normal Command Line, assumes output file as last option */
1390  ProcessCommandOptions(cli_wand,argc,argv,1);
1391  }
1392  /* ------------- */
1393 
1394 Magick_Command_Cleanup:
1395  cli_wand->location="Cleanup";
1396  cli_wand->filename=argv[0];
1397  if (cli_wand->wand.debug != MagickFalse)
1398  (void) CLILogEvent(cli_wand,CommandEvent,GetMagickModule(),
1399  "\"%s\"",argv[0]);
1400 
1401  /* recover original image_info and clean up stacks
1402  FUTURE: "-reset stacks" option */
1403  while ((cli_wand->image_list_stack != (CLIStack *) NULL) &&
1404  (cli_wand->image_list_stack->next != (CLIStack *) NULL))
1405  CLIOption(cli_wand,")");
1406  while ((cli_wand->image_info_stack != (CLIStack *) NULL) &&
1407  (cli_wand->image_info_stack->next != (CLIStack *) NULL))
1408  CLIOption(cli_wand,"}");
1409 
1410  /* assert we have recovered the original structures */
1411  assert(cli_wand->wand.image_info == image_info);
1412  assert(cli_wand->wand.exception == exception);
1413 
1414  /* Handle metadata for ImageMagickObject COM object for Windows VBS */
1415  if ((cli_wand->wand.images != (Image *) NULL) &&
1416  (metadata != (char **) NULL))
1417  {
1418  const char
1419  *format;
1420 
1421  char
1422  *text;
1423 
1424  format="%w,%h,%m"; /* Get this from image_info Option splaytree */
1425  text=InterpretImageProperties(image_info,cli_wand->wand.images,format,
1426  exception);
1427  if (text == (char *) NULL)
1428  ThrowMagickException(exception,GetMagickModule(),ResourceLimitError,
1429  "MemoryAllocationFailed","`%s'", GetExceptionMessage(errno));
1430  else
1431  {
1432  (void) ConcatenateString(&(*metadata),text);
1433  text=DestroyString(text);
1434  }
1435  }
1436 
1437 Magick_Command_Exit:
1438  cli_wand->location="Exiting";
1439  cli_wand->filename=argv[0];
1440  if (cli_wand->wand.debug != MagickFalse)
1441  (void) CLILogEvent(cli_wand,CommandEvent,GetMagickModule(),
1442  "\"%s\"",argv[0]);
1443 
1444  /* Destroy the special CLI Wand */
1445  cli_wand->wand.image_info = (ImageInfo *) NULL; /* not these */
1446  cli_wand->wand.exception = (ExceptionInfo *) NULL;
1447  cli_wand=DestroyMagickCLI(cli_wand);
1448 
1449  return(exception->severity < ErrorException ? MagickTrue : MagickFalse);
1450 }
_CLIStack
Definition: wandcli-private.h:90
ScriptTokenInfo
Definition: script-token.h:39
_MagickCLI
Definition: wandcli-private.h:101