blob: cf3a7d5e0efa63bcbb19161f5b41aa647067f7e0 [file] [log] [blame]
anthonyfa1e43d2012-02-12 12:55:45 +00001/*
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
46/*
47 Include declarations.
48*/
49#include "MagickWand/studio.h"
50#include "MagickWand/MagickWand.h"
51#include "MagickWand/magick-wand-private.h"
anthony43f425d2012-02-26 12:58:58 +000052#include "MagickWand/operation.h"
anthony2052d272012-02-28 12:48:29 +000053#include "MagickWand/operation-private.h"
54#include "MagickWand/magick-cli.h"
anthony1cdc5b72012-03-03 02:31:18 +000055#include "MagickWand/script-token.h"
anthonyfa1e43d2012-02-12 12:55:45 +000056#include "MagickCore/utility-private.h"
anthony668f43a2012-02-20 14:55:32 +000057#include "MagickCore/version.h"
58
anthony43f425d2012-02-26 12:58:58 +000059/* verbose debugging,
60 1 - option type
61 2 - source of option
anthony24aa8822012-03-11 00:56:06 +000062 3 - mnemonic lookup
63 4 - output options/artifacts
64*/
anthony6079d4b2012-03-16 00:36:45 +000065#define MagickCommandDebug 0
anthony668f43a2012-02-20 14:55:32 +000066
anthony1cdc5b72012-03-03 02:31:18 +000067#define ThrowFileException(exception,severity,tag,context) \
anthony668f43a2012-02-20 14:55:32 +000068{ \
anthonyafa3dfc2012-03-03 11:31:30 +000069 char \
70 *message; \
71 \
72 message=GetExceptionMessage(errno); \
73 (void) ThrowMagickException(exception,GetMagickModule(),severity, \
74 tag == (const char *) NULL ? "unknown" : tag,"`%s': %s",context,message); \
75 message=DestroyString(message); \
anthony668f43a2012-02-20 14:55:32 +000076}
anthony24aa8822012-03-11 00:56:06 +000077
78#if MagickCommandDebug >= 4
79static void OutputOptions(ImageInfo *image_info)
80{
81 const char
82 *option,
83 *value;
84
85 (void) FormatLocaleFile(stdout," Image_Info Options:\n");
86 ResetImageOptionIterator(image_info);
87 while ((option=GetNextImageOption(image_info)) != (const char *) NULL ) {
88 (void) FormatLocaleFile(stdout," %s: ",option);
89 value=GetImageOption(image_info,option);
90 if (value != (const char *) NULL)
91 (void) FormatLocaleFile(stdout,"%s\n",value);
92 }
93 ResetImageOptionIterator(image_info);
94}
95
96static void OutputArtifacts(Image *image)
97{
98 const char
99 *artifact,
100 *value;
101
102 (void) FormatLocaleFile(stdout," Image Artifacts:\n");
103 ResetImageArtifactIterator(image);
104 while ((artifact=GetNextImageArtifact(image)) != (const char *) NULL ) {
105 (void) FormatLocaleFile(stdout," %s: ",artifact);
106 value=GetImageArtifact(image,artifact);
107 if (value != (const char *) NULL)
108 (void) FormatLocaleFile(stdout,"%s\n",value);
109 }
110 ResetImageArtifactIterator(image);
111}
112#endif
113
anthonyfa1e43d2012-02-12 12:55:45 +0000114
115/*
116%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
117% %
118% %
119% %
anthony668f43a2012-02-20 14:55:32 +0000120+ P r o c e s s S c r i p t O p t i o n s %
anthonyfa1e43d2012-02-12 12:55:45 +0000121% %
122% %
123% %
124%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
125%
anthony668f43a2012-02-20 14:55:32 +0000126% ProcessScriptOptions() reads options and processes options as they are
127% found in the given file, or pipeline. The filename to open and read
anthony0b46ebe2012-03-06 04:15:35 +0000128% options is given as the 'index' argument of the argument array given.
anthonyfa1e43d2012-02-12 12:55:45 +0000129%
anthony0b46ebe2012-03-06 04:15:35 +0000130% Other arguments following index may be read by special script options
131% as settings (strings), images, or as operations to be processed in various
132% ways. How they are treated is up to the script being processed.
anthonyfa1e43d2012-02-12 12:55:45 +0000133%
anthony0b46ebe2012-03-06 04:15:35 +0000134% Note that a script not 'return' to the command line processing, nor can
135% they call (and return from) other scripts. At least not at this time.
136%
137% There are no 'ProcessOptionFlags' control flags at this time.
anthony668f43a2012-02-20 14:55:32 +0000138%
139% The format of the ProcessScriptOptions method is:
140%
anthony0b46ebe2012-03-06 04:15:35 +0000141% void ProcessScriptOptions(MagickCLI *cli_wand,int argc,char **argv,
142% int index)
anthonyfa1e43d2012-02-12 12:55:45 +0000143%
144% A description of each parameter follows:
145%
anthony43f425d2012-02-26 12:58:58 +0000146% o cli_wand: the main CLI Wand to use.
anthonyfa1e43d2012-02-12 12:55:45 +0000147%
148% o argc: the number of elements in the argument vector.
149%
150% o argv: A text array containing the command line arguments.
151%
anthony0b46ebe2012-03-06 04:15:35 +0000152% o index: offset for argc to CLI argumnet count
153%
anthonyfa1e43d2012-02-12 12:55:45 +0000154*/
anthony0b46ebe2012-03-06 04:15:35 +0000155WandExport void ProcessScriptOptions(MagickCLI *cli_wand,int argc,char **argv,
156 int index)
anthony668f43a2012-02-20 14:55:32 +0000157{
anthony1cdc5b72012-03-03 02:31:18 +0000158 ScriptTokenInfo
159 *token_info;
anthony668f43a2012-02-20 14:55:32 +0000160
anthony668f43a2012-02-20 14:55:32 +0000161 CommandOptionFlags
162 option_type;
163
anthony0b46ebe2012-03-06 04:15:35 +0000164 int
anthony1cdc5b72012-03-03 02:31:18 +0000165 count;
anthony668f43a2012-02-20 14:55:32 +0000166
anthony1cdc5b72012-03-03 02:31:18 +0000167 char
168 *option,
169 *arg1,
170 *arg2;
171
anthony0b46ebe2012-03-06 04:15:35 +0000172 assert(argc>index); /* at least one argument - script name */
173 assert(argv != (char **)NULL);
174 assert(argv[index] != (char *)NULL);
175 assert(argv[argc-1] != (char *)NULL);
anthony43f425d2012-02-26 12:58:58 +0000176 assert(cli_wand != (MagickCLI *) NULL);
177 assert(cli_wand->signature == WandSignature);
178 if (cli_wand->wand.debug != MagickFalse)
179 (void) LogMagickEvent(WandEvent,GetMagickModule(),"%s",cli_wand->wand.name);
anthony668f43a2012-02-20 14:55:32 +0000180
anthony0b46ebe2012-03-06 04:15:35 +0000181 /* open file script or stream, and set up tokenizer */
182 token_info = AcquireScriptTokenInfo(argv[index]);
anthony1cdc5b72012-03-03 02:31:18 +0000183 if (token_info->token == (char *) NULL) {
anthonyafa3dfc2012-03-03 11:31:30 +0000184 ThrowFileException(cli_wand->wand.exception,OptionFatalError,
anthony0b46ebe2012-03-06 04:15:35 +0000185 "UnableToOpenScript",argv[index]);
anthony1cdc5b72012-03-03 02:31:18 +0000186 return;
187 }
anthony668f43a2012-02-20 14:55:32 +0000188
anthony0b46ebe2012-03-06 04:15:35 +0000189 /* define the error location string for use in exceptions
190 order of input escapes: option, filename, line, column
191 */
192 cli_wand->location="'%s' in \"%s\" line %u column %u";
193 if ( LocaleCompare("-", argv[index]) == 0 )
194 cli_wand->filename="stdin";
195 else
196 cli_wand->filename=argv[index];
anthony668f43a2012-02-20 14:55:32 +0000197
anthony0b46ebe2012-03-06 04:15:35 +0000198 /* Process Options from Script */
199 option = arg1 = arg2 = (char*)NULL;
anthony1cdc5b72012-03-03 02:31:18 +0000200 while (1) {
anthony668f43a2012-02-20 14:55:32 +0000201
anthony1cdc5b72012-03-03 02:31:18 +0000202 /* Get a option */
anthony0b46ebe2012-03-06 04:15:35 +0000203 { MagickBooleanType status = GetScriptToken(token_info);
204 cli_wand->line=token_info->token_line;
205 cli_wand->column=token_info->token_column;
206 if( status == MagickFalse )
207 break;
208 }
anthony668f43a2012-02-20 14:55:32 +0000209
anthony1cdc5b72012-03-03 02:31:18 +0000210 /* save option details */
211 CloneString(&option,token_info->token);
anthony1cdc5b72012-03-03 02:31:18 +0000212
213 { /* get option type and argument count */
214 const OptionInfo *option_info = GetCommandOptionInfo(option);
215 count=option_info->type;
216 option_type=option_info->flags;
anthony0b46ebe2012-03-06 04:15:35 +0000217#if MagickCommandDebug >= 2
218 (void) FormatLocaleFile(stderr, "Script: %u,%u: \"%s\" matched \"%s\"\n",
219 cli_wand->line, cli_wand->line, option, option_info->mnemonic );
anthony668f43a2012-02-20 14:55:32 +0000220#endif
anthony1cdc5b72012-03-03 02:31:18 +0000221 }
anthony668f43a2012-02-20 14:55:32 +0000222
anthony0b46ebe2012-03-06 04:15:35 +0000223 /* handle a undefined option - image read? */
anthony1cdc5b72012-03-03 02:31:18 +0000224 if ( option_type == UndefinedOptionFlag ||
225 (option_type & NonMagickOptionFlag) != 0 ) {
anthony668f43a2012-02-20 14:55:32 +0000226#if MagickCommandDebug
anthonyafa3dfc2012-03-03 11:31:30 +0000227 (void) FormatLocaleFile(stderr, "Script Non-Option: \"%s\"\n", option);
anthony668f43a2012-02-20 14:55:32 +0000228#endif
anthony24aa8822012-03-11 00:56:06 +0000229 if ( IsCommandOption(option) == MagickFalse)
anthonyafa3dfc2012-03-03 11:31:30 +0000230 /* non-option -- treat as a image read */
231 CLISpecialOperator(cli_wand,"-read",option);
anthonyafa3dfc2012-03-03 11:31:30 +0000232 else
233 CLIWandExceptionBreak(OptionFatalError,"UnrecognizedOption",option);
anthony24aa8822012-03-11 00:56:06 +0000234 count = 0;
235 goto next_token;
anthonyafa3dfc2012-03-03 11:31:30 +0000236 }
anthony1cdc5b72012-03-03 02:31:18 +0000237
238 if ( count >= 1 ) {
anthonyafa3dfc2012-03-03 11:31:30 +0000239 if( GetScriptToken(token_info) == MagickFalse )
anthony24aa8822012-03-11 00:56:06 +0000240 CLIWandException(OptionFatalError,"MissingArgument",option);
anthonyafa3dfc2012-03-03 11:31:30 +0000241 CloneString(&arg1,token_info->token);
242 }
anthony1cdc5b72012-03-03 02:31:18 +0000243 else
anthonyafa3dfc2012-03-03 11:31:30 +0000244 CloneString(&arg1,(char *)NULL);
anthony2052d272012-02-28 12:48:29 +0000245
anthony1cdc5b72012-03-03 02:31:18 +0000246 if ( count >= 2 ) {
anthonyafa3dfc2012-03-03 11:31:30 +0000247 if( GetScriptToken(token_info) == MagickFalse )
248 CLIWandExceptionBreak(OptionFatalError,"MissingArgument",option);
249 CloneString(&arg2,token_info->token);
250 }
anthony1cdc5b72012-03-03 02:31:18 +0000251 else
252 CloneString(&arg2,(char *)NULL);
anthony668f43a2012-02-20 14:55:32 +0000253
anthonyafa3dfc2012-03-03 11:31:30 +0000254 /* handle script special options here */
anthony1cdc5b72012-03-03 02:31:18 +0000255 //either continue processing command line
256 // or making use of the command line options.
257 //CLICommandOptions(cli_wand,count+1,argv, MagickScriptArgsFlags);
anthony2052d272012-02-28 12:48:29 +0000258
anthony668f43a2012-02-20 14:55:32 +0000259#if MagickCommandDebug
anthony1cdc5b72012-03-03 02:31:18 +0000260 (void) FormatLocaleFile(stderr,
261 "Script Option: \"%s\" \tCount: %d Flags: %04x Args: \"%s\" \"%s\"\n",
262 option,(int) count,option_type,arg1,arg2);
anthony668f43a2012-02-20 14:55:32 +0000263#endif
anthony668f43a2012-02-20 14:55:32 +0000264
anthony1cdc5b72012-03-03 02:31:18 +0000265 /* Process non-script specific option from file */
anthonyafa3dfc2012-03-03 11:31:30 +0000266 if ( (option_type & SpecialOptionFlag) != 0 ) {
267 if ( LocaleCompare(option,"-exit") == 0 )
268 break;
anthony0b46ebe2012-03-06 04:15:35 +0000269 /* No "-script" option from script at this time */
anthonyafa3dfc2012-03-03 11:31:30 +0000270 CLISpecialOperator(cli_wand,option,arg1);
271 }
anthony668f43a2012-02-20 14:55:32 +0000272
anthonyafa3dfc2012-03-03 11:31:30 +0000273 if ( (option_type & SettingOptionFlags) != 0 ) {
274 CLISettingOptionInfo(cli_wand, option, arg1);
anthony24aa8822012-03-11 00:56:06 +0000275 // FUTURE: Sync Specific Settings into Image Properities (not global)
anthonyafa3dfc2012-03-03 11:31:30 +0000276 }
anthony668f43a2012-02-20 14:55:32 +0000277
anthony1cdc5b72012-03-03 02:31:18 +0000278 if ( (option_type & SimpleOperatorOptionFlag) != 0)
anthonyafa3dfc2012-03-03 11:31:30 +0000279 CLISimpleOperatorImages(cli_wand, option, arg1, arg2);
anthony668f43a2012-02-20 14:55:32 +0000280
anthony1cdc5b72012-03-03 02:31:18 +0000281 if ( (option_type & ListOperatorOptionFlag) != 0 )
anthonyafa3dfc2012-03-03 11:31:30 +0000282 CLIListOperatorImages(cli_wand, option, arg1, arg2);
anthony1cdc5b72012-03-03 02:31:18 +0000283
anthony24aa8822012-03-11 00:56:06 +0000284next_token:
285#if MagickCommandDebug >= 4
286 OutputOptions(cli_wand->wand.image_info);
287 if ( cli_wand->wand.images != (Image *)NULL )
288 OutputArtifacts(cli_wand->wand.images);
289#endif
anthony1cdc5b72012-03-03 02:31:18 +0000290 if ( CLICatchException(cli_wand, MagickFalse) != MagickFalse )
291 break;
292 }
293
294#if MagickCommandDebug
295 (void) FormatLocaleFile(stderr, "Script End: %d\n", token_info->status);
296#endif
297 switch( token_info->status ) {
298 case TokenStatusOK:
299 case TokenStatusEOF:
300 break;
301 case TokenStatusBadQuotes:
302 /* Ensure last token has a sane length for error report */
303 if( strlen(token_info->token) > INITAL_TOKEN_LENGTH-1 ) {
304 token_info->token[INITAL_TOKEN_LENGTH-4] = '.';
305 token_info->token[INITAL_TOKEN_LENGTH-3] = '.';
306 token_info->token[INITAL_TOKEN_LENGTH-2] = '.';
307 token_info->token[INITAL_TOKEN_LENGTH-1] = '\0';
308 }
anthonyafa3dfc2012-03-03 11:31:30 +0000309 CLIWandException(OptionFatalError,"ScriptUnbalancedQuotes",
310 token_info->token);
anthony1cdc5b72012-03-03 02:31:18 +0000311 break;
312 case TokenStatusMemoryFailed:
anthonyafa3dfc2012-03-03 11:31:30 +0000313 CLIWandException(OptionFatalError,"ScriptTokenMemoryFailed","");
anthony1cdc5b72012-03-03 02:31:18 +0000314 break;
315 case TokenStatusBinary:
anthonyafa3dfc2012-03-03 11:31:30 +0000316 CLIWandException(OptionFatalError,"ScriptIsBinary","");
anthony1cdc5b72012-03-03 02:31:18 +0000317 break;
318 }
319
320 /* Clean up */
321 token_info = DestroyScriptTokenInfo(token_info);
322
323 CloneString(&option,(char *)NULL);
324 CloneString(&arg1,(char *)NULL);
325 CloneString(&arg2,(char *)NULL);
326
327 return;
anthony668f43a2012-02-20 14:55:32 +0000328}
329
330/*
331%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
332% %
333% %
334% %
335+ P r o c e s s C o m m a n d O p t i o n s %
336% %
337% %
338% %
339%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
340%
341% ProcessCommandOptions() reads and processes arguments in the given
342% command line argument array. The array does not contain the command
343% being processed, only the options.
344%
345% The 'process_flags' can be used to control and limit option processing.
346% For example, to only process one option, or how unknown and special options
347% are to be handled, and if the last argument in array is to be regarded as a
348% final image write argument (filename or special coder).
349%
350% The format of the ProcessCommandOptions method is:
351%
anthony43f425d2012-02-26 12:58:58 +0000352% int ProcessCommandOptions(MagickCLI *cli_wand,int argc,char **argv,
anthony0b46ebe2012-03-06 04:15:35 +0000353% int index, ProcessOptionFlags process_flags )
anthony668f43a2012-02-20 14:55:32 +0000354%
355% A description of each parameter follows:
356%
anthony43f425d2012-02-26 12:58:58 +0000357% o cli_wand: the main CLI Wand to use.
anthony668f43a2012-02-20 14:55:32 +0000358%
359% o argc: the number of elements in the argument vector.
360%
361% o argv: A text array containing the command line arguments.
362%
363% o process_flags: What type of arguments we are allowed to process
364%
anthony0b46ebe2012-03-06 04:15:35 +0000365% o index: index in the argv array to start processing from
366%
367% The function returns the index ot the next option to be processed. This
368% is really only releven if process_flags contains a ProcessOneOptionOnly
369% flag.
370%
anthony668f43a2012-02-20 14:55:32 +0000371*/
anthony0b46ebe2012-03-06 04:15:35 +0000372WandExport int ProcessCommandOptions(MagickCLI *cli_wand, int argc,
373 char **argv, int index, ProcessOptionFlags process_flags )
anthonyfa1e43d2012-02-12 12:55:45 +0000374{
375 const char
376 *option,
377 *arg1,
378 *arg2;
379
anthony0b46ebe2012-03-06 04:15:35 +0000380 int
anthonyfa1e43d2012-02-12 12:55:45 +0000381 i,
anthony668f43a2012-02-20 14:55:32 +0000382 end,
anthonyfa1e43d2012-02-12 12:55:45 +0000383 count;
384
385 CommandOptionFlags
anthony686b1a32012-02-15 14:50:53 +0000386 option_type;
anthonyfa1e43d2012-02-12 12:55:45 +0000387
anthony0b46ebe2012-03-06 04:15:35 +0000388 assert(argc>=index); /* you may have no arguments left! */
389 assert(argv != (char **)NULL);
390 assert(argv[index] != (char *)NULL);
391 assert(argv[argc-1] != (char *)NULL);
anthony43f425d2012-02-26 12:58:58 +0000392 assert(cli_wand != (MagickCLI *) NULL);
393 assert(cli_wand->signature == WandSignature);
394 if (cli_wand->wand.debug != MagickFalse)
395 (void) LogMagickEvent(WandEvent,GetMagickModule(),"%s",cli_wand->wand.name);
anthonyfa1e43d2012-02-12 12:55:45 +0000396
397 /*
398 Parse command-line options.
399 */
anthonyafa3dfc2012-03-03 11:31:30 +0000400 cli_wand->location="'%s' %s arg %d";
401 cli_wand->filename="CLI";
402
anthony668f43a2012-02-20 14:55:32 +0000403 end = argc;
404 if ( ( process_flags & ProcessOutputFile ) != 0 )
405 end--;
anthony0b46ebe2012-03-06 04:15:35 +0000406
407 for (i=index; i < end; i += count +1) {
anthonyafa3dfc2012-03-03 11:31:30 +0000408 /* Finished processing one option? */
anthony0b46ebe2012-03-06 04:15:35 +0000409 if ( ( process_flags & ProcessOneOptionOnly ) != 0 && i != index )
410 return(i);
anthony668f43a2012-02-20 14:55:32 +0000411
anthonyafa3dfc2012-03-03 11:31:30 +0000412 option=argv[i];
413 cli_wand->line=i;
anthonyfa1e43d2012-02-12 12:55:45 +0000414
anthonyafa3dfc2012-03-03 11:31:30 +0000415 { const OptionInfo *option_info = GetCommandOptionInfo(argv[i]);
416 count=option_info->type;
417 option_type=option_info->flags;
anthony0b46ebe2012-03-06 04:15:35 +0000418#if MagickCommandDebug >= 2
419 (void) FormatLocaleFile(stderr, "CLI %d: \"%s\" matched \"%s\"\n",
420 i, argv[i], option_info->mnemonic );
anthony686b1a32012-02-15 14:50:53 +0000421#endif
anthonyafa3dfc2012-03-03 11:31:30 +0000422 }
anthonyfa1e43d2012-02-12 12:55:45 +0000423
anthonyafa3dfc2012-03-03 11:31:30 +0000424 if ( option_type == UndefinedOptionFlag ||
425 (option_type & NonMagickOptionFlag) != 0 ) {
anthonyfa1e43d2012-02-12 12:55:45 +0000426#if MagickCommandDebug
anthonyafa3dfc2012-03-03 11:31:30 +0000427 (void) FormatLocaleFile(stderr, "CLI Non-Option: \"%s\"\n", option);
anthonyfa1e43d2012-02-12 12:55:45 +0000428#endif
anthonyafa3dfc2012-03-03 11:31:30 +0000429 if ( ( IsCommandOption(option) == MagickFalse ) &&
anthony24aa8822012-03-11 00:56:06 +0000430 ( (process_flags & ProcessNonOptionImageRead) != 0 ) )
anthonyafa3dfc2012-03-03 11:31:30 +0000431 /* non-option -- treat as a image read */
432 CLISpecialOperator(cli_wand,"-read",option);
anthonyafa3dfc2012-03-03 11:31:30 +0000433 else if ( (process_flags & ProcessUnknownOptionError) != 0 )
434 CLIWandException(OptionFatalError,"UnrecognizedOption",option);
anthony24aa8822012-03-11 00:56:06 +0000435 count = 0;
436 goto next_argument;
anthonyfa1e43d2012-02-12 12:55:45 +0000437 }
438
anthonyafa3dfc2012-03-03 11:31:30 +0000439 if ( (option_type & DeprecateOptionFlag) != 0 ) {
440 CLIWandException(OptionWarning,"DeprecatedOption",option);
441 if ( CLICatchException(cli_wand, MagickFalse) != MagickFalse )
anthony0b46ebe2012-03-06 04:15:35 +0000442 return(i+count+1);
anthonyafa3dfc2012-03-03 11:31:30 +0000443 }
444 if ((i+count) >= end ) {
anthony0b46ebe2012-03-06 04:15:35 +0000445 CLIWandException(OptionFatalError,"MissingArgument",option);
anthonyafa3dfc2012-03-03 11:31:30 +0000446 if ( CLICatchException(cli_wand, MagickFalse) != MagickFalse )
anthony0b46ebe2012-03-06 04:15:35 +0000447 return(end);
anthony24aa8822012-03-11 00:56:06 +0000448 goto next_argument; /* no more arguments unable to proceed */
anthonyafa3dfc2012-03-03 11:31:30 +0000449 }
450
451 arg1 = ( count >= 1 ) ? argv[i+1] : (char *)NULL;
452 arg2 = ( count >= 2 ) ? argv[i+2] : (char *)NULL;
453
454#if MagickCommandDebug
455 (void) FormatLocaleFile(stderr,
456 "CLI Option: \"%s\" \tCount: %d Flags: %04x Args: \"%s\" \"%s\"\n",
457 option,(int) count,option_type,arg1,arg2);
458#endif
459
460 if ( (option_type & SpecialOptionFlag) != 0 ) {
461 if ( ( process_flags & ProcessExitOption ) != 0
462 && LocaleCompare(option,"-exit") == 0 )
anthony0b46ebe2012-03-06 04:15:35 +0000463 return(i+count);
anthonyafa3dfc2012-03-03 11:31:30 +0000464 if ( ( process_flags & ProcessScriptOption ) != 0
anthony0b46ebe2012-03-06 04:15:35 +0000465 && LocaleCompare(option,"-script") == 0) {
466 // Unbalanced Parenthesis if stack not empty
467 // Call Script, with a filename as a zeroth argument
468 ProcessScriptOptions(cli_wand,argc,argv,i+1);
469 return(argc); /* no more options after script process! */
470 }
anthonyafa3dfc2012-03-03 11:31:30 +0000471 CLISpecialOperator(cli_wand,option,arg1);
472 }
473
474 if ( (option_type & SettingOptionFlags) != 0 ) {
475 CLISettingOptionInfo(cli_wand, option, arg1);
anthony24aa8822012-03-11 00:56:06 +0000476 // FUTURE: Sync Specific Settings into Image Properities (not global)
anthonyafa3dfc2012-03-03 11:31:30 +0000477 }
478
479 if ( (option_type & SimpleOperatorOptionFlag) != 0)
480 CLISimpleOperatorImages(cli_wand, option, arg1, arg2);
481
482 if ( (option_type & ListOperatorOptionFlag) != 0 )
483 CLIListOperatorImages(cli_wand, option, arg1, arg2);
484
anthony24aa8822012-03-11 00:56:06 +0000485next_argument:
486#if MagickCommandDebug >= 4
487 OutputOptions(cli_wand->wand.image_info);
488 if ( cli_wand->wand.images != (Image *)NULL )
489 OutputArtifacts(cli_wand->wand.images);
490#endif
anthonyafa3dfc2012-03-03 11:31:30 +0000491 if ( CLICatchException(cli_wand, MagickFalse) != MagickFalse )
anthony0b46ebe2012-03-06 04:15:35 +0000492 return(i+count);
anthonyafa3dfc2012-03-03 11:31:30 +0000493 }
anthony0b46ebe2012-03-06 04:15:35 +0000494 assert(i==end);
anthony2052d272012-02-28 12:48:29 +0000495
anthony668f43a2012-02-20 14:55:32 +0000496 if ( ( process_flags & ProcessOutputFile ) == 0 )
anthony0b46ebe2012-03-06 04:15:35 +0000497 return(end);
498
anthony668f43a2012-02-20 14:55:32 +0000499 assert(end==argc-1);
500
501 /*
anthony43f425d2012-02-26 12:58:58 +0000502 Implicit Write of images to final CLI argument
anthony668f43a2012-02-20 14:55:32 +0000503 */
504 option=argv[i];
anthony799889a2012-03-11 11:00:32 +0000505 cli_wand->line=i;
anthony686b1a32012-02-15 14:50:53 +0000506
anthonyfa1e43d2012-02-12 12:55:45 +0000507#if MagickCommandDebug
anthony1cdc5b72012-03-03 02:31:18 +0000508 (void) FormatLocaleFile(stderr, "CLI Write File: \"%s\"\n", option );
anthonyfa1e43d2012-02-12 12:55:45 +0000509#endif
510
511 // if stacks are not empty
512 // ThrowConvertException(OptionError,"UnbalancedParenthesis",option,i);
513
anthony668f43a2012-02-20 14:55:32 +0000514 /* This is a valid 'do no write' option for a CLI */
anthonyfa1e43d2012-02-12 12:55:45 +0000515 if (LocaleCompare(option,"-exit") == 0 )
anthony0b46ebe2012-03-06 04:15:35 +0000516 return(argc); /* just exit, no image write */
anthonyfa1e43d2012-02-12 12:55:45 +0000517
518 /* If there is an option -- produce an error */
anthony0b46ebe2012-03-06 04:15:35 +0000519 if (IsCommandOption(option) != MagickFalse) {
520 CLIWandException(OptionError,"MissingOutputFilename",option);
521 return(argc);
522 }
anthonyfa1e43d2012-02-12 12:55:45 +0000523
anthony43f425d2012-02-26 12:58:58 +0000524 /* If no images in MagickCLI */
anthony1cdc5b72012-03-03 02:31:18 +0000525 if ( cli_wand->wand.images == (Image *) NULL ) {
anthonyafa3dfc2012-03-03 11:31:30 +0000526 /* a "null:" output coder with no images is not an error! */
527 if ( LocaleCompare(option,"null:") == 0 )
anthony0b46ebe2012-03-06 04:15:35 +0000528 return(argc);
529 CLIWandException(OptionError,"NoImagesForFinalWrite",option);
530 return(argc);
anthonyafa3dfc2012-03-03 11:31:30 +0000531 }
anthonyfa1e43d2012-02-12 12:55:45 +0000532
anthony43f425d2012-02-26 12:58:58 +0000533#if 0
anthonyafa3dfc2012-03-03 11:31:30 +0000534 WandListOperatorImages(cli_wand,"-write",option,(const char *)NULL);
anthony43f425d2012-02-26 12:58:58 +0000535#else
536 (void) SyncImagesSettings(cli_wand->wand.image_info,cli_wand->wand.images,
537 cli_wand->wand.exception);
538 (void) WriteImages(cli_wand->wand.image_info,cli_wand->wand.images,option,
539 cli_wand->wand.exception);
540#endif
anthony0b46ebe2012-03-06 04:15:35 +0000541 return(argc);
anthonyfa1e43d2012-02-12 12:55:45 +0000542}
543
544/*
545%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
546% %
547% %
548% %
549+ M a g i c k I m a g e C o m m a n d %
550% %
551% %
552% %
553%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
554%
anthony668f43a2012-02-20 14:55:32 +0000555% MagickImageCommand() Handle special use CLI arguments and prepare a
anthony43f425d2012-02-26 12:58:58 +0000556% CLI MagickCLI to process the command line or directly specified script.
anthony668f43a2012-02-20 14:55:32 +0000557%
558% This is essentualy interface function between the MagickCore library
anthony43f425d2012-02-26 12:58:58 +0000559% initialization function MagickCommandGenesis(), and the option MagickCLI
anthony668f43a2012-02-20 14:55:32 +0000560% processing functions ProcessCommandOptions() or ProcessScriptOptions()
anthonyfa1e43d2012-02-12 12:55:45 +0000561%
562% The format of the MagickImageCommand method is:
563%
564% MagickBooleanType MagickImageCommand(ImageInfo *image_info,
565% int argc, char **argv, char **metadata, ExceptionInfo *exception)
566%
567% A description of each parameter follows:
568%
569% o image_info: the starting image_info structure
570% (for compatibilty with MagickCommandGenisis())
571%
572% o argc: the number of elements in the argument vector.
573%
574% o argv: A text array containing the command line arguments.
575%
576% o metadata: any metadata is returned here.
577% (for compatibilty with MagickCommandGenisis())
578%
579% o exception: return any errors or warnings in this structure.
580%
581*/
582
583static MagickBooleanType MagickUsage(void)
584{
585 printf("Version: %s\n",GetMagickVersion((size_t *) NULL));
586 printf("Copyright: %s\n",GetMagickCopyright());
587 printf("Features: %s\n\n",GetMagickFeatures());
588 printf("\n");
589
590 printf("Usage: %s [(options|images) ...] output_image\n", GetClientName());
591 printf(" %s -script filename [script args...]\n", GetClientName());
592 printf(" ... | %s -script - | ...\n", GetClientName());
593 printf("\n");
594
595 printf(" For more information on usage, options, examples, and technqiues\n");
596 printf(" see the ImageMagick website at\n %s\n", MagickAuthoritativeURL);
597 printf(" Or the web pages in ImageMagick Sources\n");
598 return(MagickFalse);
599}
600
601/*
602 Concatanate given file arguments to the given output argument.
603 Used for a special -concatenate option used for specific 'delegates'.
604 The option is not formally documented.
605
606 magick -concatenate files... output
607
608 This is much like the UNIX "cat" command, but for both UNIX and Windows,
609 however the last argument provides the output filename.
610*/
anthonyfa1e43d2012-02-12 12:55:45 +0000611static MagickBooleanType ConcatenateImages(int argc,char **argv,
612 ExceptionInfo *exception)
613{
614 FILE
615 *input,
616 *output;
617
618 int
619 c;
620
621 register ssize_t
622 i;
623
624 output=fopen_utf8(argv[argc-1],"wb");
anthonyafa3dfc2012-03-03 11:31:30 +0000625 if (output == (FILE *) NULL) {
626 ThrowFileException(exception,FileOpenError,"UnableToOpenFile",
627 argv[argc-1]);
628 return(MagickFalse);
629 }
630 for (i=2; i < (ssize_t) (argc-1); i++) {
anthonyfa1e43d2012-02-12 12:55:45 +0000631 input=fopen_utf8(argv[i],"rb");
632 if (input == (FILE *) NULL)
633 ThrowFileException(exception,FileOpenError,"UnableToOpenFile",argv[i]);
634 for (c=fgetc(input); c != EOF; c=fgetc(input))
635 (void) fputc((char) c,output);
636 (void) fclose(input);
637 (void) remove_utf8(argv[i]);
638 }
639 (void) fclose(output);
640 return(MagickTrue);
641}
642
643WandExport MagickBooleanType MagickImageCommand(ImageInfo *image_info,
644 int argc,char **argv,char **metadata,ExceptionInfo *exception)
645{
anthony43f425d2012-02-26 12:58:58 +0000646 MagickCLI
647 *cli_wand;
anthonyfa1e43d2012-02-12 12:55:45 +0000648
649 const char
650 *option;
651
652 /* Handle special single use options */
anthonyafa3dfc2012-03-03 11:31:30 +0000653 if (argc == 2) {
654 option=argv[1];
655 if ((LocaleCompare("-version",option+1) == 0) ||
656 (LocaleCompare("--version",option+1) == 0) ) {
657 (void) FormatLocaleFile(stdout,"Version: %s\n",
658 GetMagickVersion((size_t *) NULL));
659 (void) FormatLocaleFile(stdout,"Copyright: %s\n",
660 GetMagickCopyright());
661 (void) FormatLocaleFile(stdout,"Features: %s\n\n",
662 GetMagickFeatures());
663 return(MagickFalse);
anthonyfa1e43d2012-02-12 12:55:45 +0000664 }
anthonyafa3dfc2012-03-03 11:31:30 +0000665 }
anthony668f43a2012-02-20 14:55:32 +0000666 /* The "magick" command must have at least two arguments */
anthonyfa1e43d2012-02-12 12:55:45 +0000667 if (argc < 3)
668 return(MagickUsage());
669 ReadCommandlLine(argc,&argv);
anthony668f43a2012-02-20 14:55:32 +0000670
anthonyfa1e43d2012-02-12 12:55:45 +0000671#if 0
anthony668f43a2012-02-20 14:55:32 +0000672 /* FUTURE: This does not make sense! Remove it.
anthony43f425d2012-02-26 12:58:58 +0000673 Only a 'image read' needs to expand file name glob patterns
anthony668f43a2012-02-20 14:55:32 +0000674 */
anthonyfa1e43d2012-02-12 12:55:45 +0000675 status=ExpandFilenames(&argc,&argv);
676 if (status == MagickFalse)
677 ThrowConvertException(ResourceLimitError,"MemoryAllocationFailed",
678 GetExceptionMessage(errno));
679#endif
anthony686b1a32012-02-15 14:50:53 +0000680
anthony43f425d2012-02-26 12:58:58 +0000681 /* Special option (hidden) for delegate usage - no wand needed */
anthonyfa1e43d2012-02-12 12:55:45 +0000682 if (LocaleCompare("-concatenate",argv[1]) == 0)
683 return(ConcatenateImages(argc,argv,exception));
684
anthony668f43a2012-02-20 14:55:32 +0000685 /* Initialize special "CLI Wand" to hold images and settings (empty) */
anthonyfa1e43d2012-02-12 12:55:45 +0000686 /* FUTURE: add this to 'operations.c' */
anthony43f425d2012-02-26 12:58:58 +0000687 cli_wand=AcquireMagickCLI(image_info,exception);
anthonyfa1e43d2012-02-12 12:55:45 +0000688
anthony1cdc5b72012-03-03 02:31:18 +0000689 if (LocaleCompare("-list",argv[1]) == 0)
690 /* Special option, list information and exit
691 FUTURE: this should be a MagickCore option,
692 especially as no wand is actually needed!
693 */
anthonyafa3dfc2012-03-03 11:31:30 +0000694 CLISpecialOperator(cli_wand, argv[1], argv[2]);
695 else if (LocaleCompare("-script",argv[1]) == 0) {
696 /* Start processing directly from script, no pre-script options
anthony0b46ebe2012-03-06 04:15:35 +0000697 Replace wand command name with script name
698 First argument in the argv array is the script name to read.
anthonyafa3dfc2012-03-03 11:31:30 +0000699 */
700 GetPathComponent(argv[2],TailPath,cli_wand->wand.name);
anthony0b46ebe2012-03-06 04:15:35 +0000701 ProcessScriptOptions(cli_wand,argc,argv,2);
anthonyafa3dfc2012-03-03 11:31:30 +0000702 }
anthony0b46ebe2012-03-06 04:15:35 +0000703 else {
anthony43f425d2012-02-26 12:58:58 +0000704 /* Processing Command line, assuming output file as last option */
anthony0b46ebe2012-03-06 04:15:35 +0000705 GetPathComponent(argv[0],TailPath,cli_wand->wand.name);
706 ProcessCommandOptions(cli_wand,argc,argv,1,MagickCommandOptionFlags);
707 }
anthonyfa1e43d2012-02-12 12:55:45 +0000708
anthony1cdc5b72012-03-03 02:31:18 +0000709 /* recover original image_info from bottom of stack */
anthony43f425d2012-02-26 12:58:58 +0000710 while (cli_wand->image_info_stack != (Stack *)NULL)
711 CLISpecialOperator(cli_wand,"}",(const char *)NULL);
anthony2052d272012-02-28 12:48:29 +0000712
anthony1cdc5b72012-03-03 02:31:18 +0000713 /* assert we have recovered the original structures */
anthony43f425d2012-02-26 12:58:58 +0000714 assert(cli_wand->wand.image_info == image_info);
715 assert(cli_wand->wand.exception == exception);
anthonyfa1e43d2012-02-12 12:55:45 +0000716
717 /* Handle metadata for ImageMagickObject COM object for Windows VBS */
anthonyafa3dfc2012-03-03 11:31:30 +0000718 if (metadata != (char **) NULL) {
719 const char
720 *format;
anthonyfa1e43d2012-02-12 12:55:45 +0000721
anthonyafa3dfc2012-03-03 11:31:30 +0000722 char
723 *text;
anthonyfa1e43d2012-02-12 12:55:45 +0000724
anthonyafa3dfc2012-03-03 11:31:30 +0000725 format="%w,%h,%m"; // Get this from image_info Option splaytree
anthonyfa1e43d2012-02-12 12:55:45 +0000726
anthonyafa3dfc2012-03-03 11:31:30 +0000727 text=InterpretImageProperties(image_info,cli_wand->wand.images,format,
728 exception);
729 if (text == (char *) NULL)
730 ThrowMagickException(exception,GetMagickModule(),ResourceLimitError,
731 "MemoryAllocationFailed","`%s'", GetExceptionMessage(errno));
732 else {
733 (void) ConcatenateString(&(*metadata),text);
734 text=DestroyString(text);
anthonyfa1e43d2012-02-12 12:55:45 +0000735 }
anthonyafa3dfc2012-03-03 11:31:30 +0000736 }
anthonyfa1e43d2012-02-12 12:55:45 +0000737 /* Destroy the special CLI Wand */
anthony43f425d2012-02-26 12:58:58 +0000738 cli_wand->wand.image_info = (ImageInfo *)NULL; /* not these */
739 cli_wand->wand.exception = (ExceptionInfo *)NULL;
740 cli_wand=DestroyMagickCLI(cli_wand);
anthonyfa1e43d2012-02-12 12:55:45 +0000741
742 return((exception->severity > ErrorException) ? MagickFalse : MagickTrue);
743}