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