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