2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5 % SSS CCC RRRR III PPPP TTTTT TTTTT OOO K K EEEE N N %
6 % S C R R I P P T T O O K K E NN N %
7 % SSS C RRRR I PPPP T T O O KK EEE N N N %
8 % S C R R I P T T O O K K E N NN %
9 % SSSS CCC R RR III P T T OOO K K EEEE N N %
11 % Perform "Magick" on Images via the Command Line Interface %
18 % Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization %
19 % dedicated to making software imaging solutions freely available. %
21 % You may not use this file except in compliance with the License. You may %
22 % obtain a copy of the License at %
24 % http://www.imagemagick.org/script/license.php %
26 % Unless required by applicable law or agreed to in writing, software %
27 % distributed under the License is distributed on an "AS IS" BASIS, %
28 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
29 % See the License for the specific language governing permissions and %
30 % limitations under the License. %
32 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
34 % Read a stream of characters and return tokens one at a time
41 NOTE: Do not include if being compiled into the "test/script-token-test.c"
42 module, for low level token testing.
44 #ifndef SCRIPT_TOKEN_TESTING
45 # include "MagickWand/studio.h"
46 # include "MagickWand/MagickWand.h"
47 # include "MagickWand/script-token.h"
48 # include "MagickCore/string-private.h"
49 # include "MagickCore/utility-private.h"
53 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
57 % A c q u i r e S c r i p t T o k e n I n f o %
61 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
63 % AcquireScriptTokenInfo() allocated, initializes and opens the given
64 % file stream from which tokens are to be extracted.
66 % The format of the AcquireScriptTokenInfo method is:
68 % ScriptTokenInfo *AcquireScriptTokenInfo(char *filename)
70 % A description of each parameter follows:
72 % o filename the filename to open ("-" means stdin)
75 WandExport ScriptTokenInfo *AcquireScriptTokenInfo(char *filename)
80 token_info=(ScriptTokenInfo *) AcquireMagickMemory(sizeof(*token_info));
81 if (token_info == (ScriptTokenInfo *) NULL)
83 (void) ResetMagickMemory(token_info,0,sizeof(*token_info));
85 token_info->opened=MagickFalse;
86 if ( LocaleCompare(filename,"-") == 0 ) {
87 token_info->stream=stdin;
88 token_info->opened=MagickFalse;
90 #if 0 /* FUTURE POSIBILITIES */
91 else if ( LocaleNCompare(filename,"fd:",3) == 0 ) {
92 token_info->stream=fdopen(StringToLong(filename+3),"r");
93 token_info->opened=MagickFalse;
97 token_info->stream=fopen(filename, "r");
99 if ( token_info->stream == (FILE *)NULL ) {
100 token_info=(ScriptTokenInfo *) RelinquishMagickMemory(token_info);
104 token_info->curr_line=1;
105 token_info->length=INITAL_TOKEN_LENGTH;
106 token_info->token=(char *) AcquireMagickMemory(token_info->length);
108 token_info->status=(token_info->token != (char *)NULL)
109 ? TokenStatusOK : TokenStatusMemoryFailed;
110 token_info->signature=WandSignature;
116 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
120 % D e s t r o y S c r i p t T o k e n I n f o %
124 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
126 % DestroyScriptTokenInfo() allocated, initializes and opens the given
127 % file stream from which tokens are to be extracted.
129 % The format of the DestroyScriptTokenInfo method is:
131 % ScriptTokenInfo *DestroyScriptTokenInfo(ScriptTokenInfo *token_info)
133 % A description of each parameter follows:
135 % o token_info The ScriptTokenInfo structure to be destroyed
138 WandExport ScriptTokenInfo * DestroyScriptTokenInfo(ScriptTokenInfo *token_info)
140 assert(token_info != (ScriptTokenInfo *) NULL);
141 assert(token_info->signature == WandSignature);
143 if ( token_info->opened != MagickFalse )
144 fclose(token_info->stream);
146 if (token_info->token != (char *) NULL )
147 token_info->token=(char *) RelinquishMagickMemory(token_info->token);
148 token_info=(ScriptTokenInfo *) RelinquishMagickMemory(token_info);
153 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
157 % G e t S c r i p t T o k e n %
161 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
163 % GetScriptToken() is fairly general, finite state token parser. That will
164 % divide a input file stream into tokens, in a way that is as close to a
165 % UNIX shell, as is feasable. Only shell variable, and command
166 % substitutions will not be performed. Tokens can be any length.
168 % Tokens are white space separated, and may be quoted, or even partially
169 % quoted by either single or double quotes, or the use of backslashes,
170 % or any mix of the three.
172 % For example: This\ is' a 'single" token"
174 % A token is returned immediatally the end of token is found. That is as soon
175 % as a unquoted white-space or EOF condition has been found. That is to say
176 % the file stream is parsed purely character-by-character, regardless any
177 % buffering constraints set by the system. It is not parsed line-by-line.
179 % The function will return 'MagickTrue' if a valid token was found, while
180 % the token status will be set accordingally to 'OK' or 'EOF', according to
181 % the cause of the end of token. The token may be an empty string if the
182 % input was a quoted empty string. Other error conditions return a value of
183 % MagickFalse, indicating any token found but was incomplete due to some
186 % Single quotes will preserve all characters including backslashes. Double
187 % quotes will also preserve backslashes unless escaping a double quote,
188 % or another backslashes. Other shell meta-characters are not treated as
189 % special by this tokenizer.
191 % For example Quoting the quote chars:
192 % \' "'" \" '"' "\"" \\ '\' "\\"
194 % Outside quotes, backslash characters will make spaces, tabs and quotes part
195 % of a token returned. However a backslash at the end of a line (and outside
196 % quotes) will cause the newline to be completely ignored (as per the shell
197 % line continuation).
199 % Comments start with a '#' character at the start of a new token, will be
200 % completely ignored upto the end of line, regardless of any backslash at the
201 % end of the line. You can escape a comment '#', using quotes or backlsashes
202 % just as you can in a shell.
204 % As a special case a ':' at the start of a line is also treated as a comment
205 % This allows a magick script to ignore a line that is parsed by the shell
206 % and can be used as a 'launcher' for magick scripts. For example
210 % # Shell Launcher for Magick Script
211 % : echo "This part is run in the shell"
212 % : exec magick -script "$0" "$@"; exit 10
214 % # The rest of the script is magick script
215 % -read label:"This is a Magick Script!"
219 % The format of the GetScriptToken method is:
221 % MagickBooleanType GetScriptToken(ScriptTokenInfo *token_info)
223 % A description of each parameter follows:
225 % o token_info pointer to a structure holding token details
228 /* States of the parser */
234 /* Macro to read character from stream
236 This also keeps track of the line and column counts.
237 The EOL is defined as either '\r\n', or '\r', or '\n'.
238 A '\r' on its own is converted into a '\n' to correctly handle
239 raw input, typically due to 'copy-n-paste' of text files.
243 c=fgetc(token_info->stream); \
244 token_info->curr_column++; \
246 c=fgetc(token_info->stream); \
247 ungetc(c,token_info->stream); \
248 c = (c!='\n')?'\n':'\r'; \
251 token_info->curr_line++, token_info->curr_column=0; \
254 if ( (c>='\0' && c<'\a') || (c>'\r' && c<' ' && c!='\033') ) { \
255 token_info->status=TokenStatusBinary; \
259 /* macro to collect the token characters */
260 #define SaveChar(c) \
262 if ((size_t) offset >= (token_info->length-1)) { \
263 if ( token_info->length >= MaxTextExtent ) \
264 token_info->length += MaxTextExtent; \
266 token_info->length *= 4; \
267 token_info->token = (char *) \
268 ResizeMagickMemory(token_info->token, token_info->length); \
269 if ( token_info->token == (char *)NULL ) { \
270 token_info->status=TokenStatusMemoryFailed; \
274 token_info->token[offset++]=(char) (c); \
277 WandExport MagickBooleanType GetScriptToken(ScriptTokenInfo *token_info)
289 /* EOF - no more tokens! */
290 if (token_info->status != TokenStatusOK)
292 token_info->token[0]='\0';
304 /* hash comment handling */
305 if ( state == IN_COMMENT ) {
310 if ( state == IN_WHITE )
311 if (c == '#' || (c == ':' && token_info->curr_column==1))
313 /* whitespace break character */
314 if (strchr(" \n\r\t",c) != (char *)NULL) {
317 token_info->token[offset]='\0';
325 /* quote character */
326 if (strchr("'\"",c) != (char *)NULL) {
329 token_info->token_line=token_info->curr_line;
330 token_info->token_column=token_info->curr_column;
347 /* escape char (preserve in quotes - unless escaping the same quote) */
350 if ( state==IN_QUOTE && quote == '\'' ) {
355 if (c == '\n' || c == '\r' )
358 state=IN_WHITE; /* end comment */
361 continue; /* line continuation (outside quotes and comment) */
365 token_info->token_line=token_info->curr_line;
366 token_info->token_column=token_info->curr_column;
370 if (c != quote && c != '\\')
377 /* ordinary character */
380 token_info->token_line=token_info->curr_line;
381 token_info->token_column=token_info->curr_column;
391 /* input stream has EOF or produced a fatal error */
392 token_info->token[offset]='\0';
393 if ( token_info->status != TokenStatusOK )
394 return(MagickFalse); /* fatal condition - no valid token */
395 token_info->status = TokenStatusEOF;
396 if ( state == IN_QUOTE)
397 token_info->status = TokenStatusBadQuotes;
398 if ( state == IN_TOKEN)
399 return(MagickTrue); /* token with EOF at end - no problem */
400 return(MagickFalse); /* in white space or in quotes - invalid token */