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