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