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