]>
git.saurik.com Git - wxWidgets.git/blob - src/stc/scintilla/lexers/LexTCMD.cxx
1 // Scintilla\ source code edit control
3 ** Lexer for Take Command / TCC batch scripts (.bat, .btm, .cmd).
5 // Written by Rex Conn (rconn [at] jpsoft [dot] com)
6 // based on the CMD lexer
7 // The License.txt file describes the conditions under which this software may be distributed.
17 #include "Scintilla.h"
21 #include "LexAccessor.h"
23 #include "StyleContext.h"
24 #include "CharacterSet.h"
25 #include "LexerModule.h"
28 using namespace Scintilla
;
32 static bool IsAlphabetic ( int ch
) {
33 return isascii ( ch
) && isalpha ( ch
);
36 static inline bool AtEOL ( Accessor
& styler
, unsigned int i
) {
37 return ( styler
[ i
] == ' \n ' ) || (( styler
[ i
] == ' \r ' ) && ( styler
. SafeGetCharAt ( i
+ 1 ) != ' \n ' ));
40 // Tests for BATCH Operators
41 static bool IsBOperator ( char ch
) {
42 return ( ch
== '=' ) || ( ch
== '+' ) || ( ch
== '>' ) || ( ch
== '<' ) || ( ch
== '|' ) || ( ch
== '&' ) || ( ch
== '!' ) || ( ch
== '?' ) || ( ch
== '*' ) || ( ch
== '(' ) || ( ch
== ')' );
45 // Tests for BATCH Separators
46 static bool IsBSeparator ( char ch
) {
47 return ( ch
== ' \\ ' ) || ( ch
== '.' ) || ( ch
== ';' ) || ( ch
== ' ' ) || ( ch
== ' \t ' ) || ( ch
== '[' ) || ( ch
== ']' ) || ( ch
== ' \" ' ) || ( ch
== ' \' ' ) || ( ch
== '/' );
50 // Tests for Environment Variable symbol
51 static inline bool IsEnvironmentVar ( char ch
) {
52 return isalpha ( ch
) || isdigit ( ch
) || ( ch
== '_' ) || ( ch
== '$' );
55 // Find length of CMD FOR variable with modifier (%~...) or return 0
56 static unsigned int GetBatchVarLen ( char * wordBuffer
)
59 if ( wordBuffer
[ 0 ] == '%' ) {
61 if ( wordBuffer
[ 1 ] == '~' )
63 else if (( wordBuffer
[ 1 ] == '%' ) && ( wordBuffer
[ 2 ] == '~' ))
68 for ( ; ( wordBuffer
[ nLength
] ); nLength
++ ) {
70 switch ( toupper ( wordBuffer
[ nLength
]) ) {
76 // fully qualified path name
84 // date / time of file
86 // file extension only
100 static void ColouriseTCMDLine ( char * lineBuffer
, unsigned int lengthLine
, unsigned int startLine
, unsigned int endPos
, WordList
* keywordlists
[], Accessor
& styler
)
102 unsigned int offset
= 0 ; // Line Buffer Offset
103 char wordBuffer
[ 260 ]; // Word Buffer - large to catch long paths
104 unsigned int wbl
; // Word Buffer Length
105 unsigned int wbo
; // Word Buffer Offset - also Special Keyword Buffer Length
106 WordList
& keywords
= * keywordlists
[ 0 ]; // Internal Commands
107 // WordList &keywords2 = *keywordlists[1]; // Aliases (optional)
108 bool isDelayedExpansion
= 1 ; // !var!
110 bool continueProcessing
= true ; // Used to toggle Regular Keyword Checking
111 // Special Keywords are those that allow certain characters without whitespace after the command
112 // Examples are: cd. cd\ echo: echo. path=
113 bool inString
= false ; // Used for processing while ""
114 // Special Keyword Buffer used to determine if the first n characters is a Keyword
115 char sKeywordBuffer
[ 260 ]; // Special Keyword Buffer
116 bool sKeywordFound
; // Exit Special Keyword for-loop if found
118 // Skip leading whitespace
119 while (( offset
< lengthLine
) && ( isspacechar ( lineBuffer
[ offset
]))) {
122 // Colorize Default Text
123 styler
. ColourTo ( startLine
+ offset
- 1 , SCE_TCMD_DEFAULT
);
125 if ( offset
>= lengthLine
)
128 // Check for Fake Label (Comment) or Real Label - return if found
129 if ( lineBuffer
[ offset
] == ':' ) {
130 if ( lineBuffer
[ offset
+ 1 ] == ':' ) {
131 // Colorize Fake Label (Comment) - :: is the same as REM
132 styler
. ColourTo ( endPos
, SCE_TCMD_COMMENT
);
134 // Colorize Real Label
135 styler
. ColourTo ( endPos
, SCE_TCMD_LABEL
);
139 // Check for Comment - return if found
140 } else if (( CompareNCaseInsensitive ( lineBuffer
+ offset
, "rem" , 3 ) == 0 ) && (( lineBuffer
[ offset
+ 3 ] == 0 ) || ( isspace ( lineBuffer
[ offset
+ 3 ] )))) {
141 styler
. ColourTo ( endPos
, SCE_TCMD_COMMENT
);
144 // Check for Drive Change (Drive Change is internal command) - return if found
145 } else if (( IsAlphabetic ( lineBuffer
[ offset
])) &&
146 ( lineBuffer
[ offset
+ 1 ] == ':' ) &&
147 (( isspacechar ( lineBuffer
[ offset
+ 2 ])) ||
148 ((( lineBuffer
[ offset
+ 2 ] == ' \\ ' )) &&
149 ( isspacechar ( lineBuffer
[ offset
+ 3 ]))))) {
150 // Colorize Regular Keyword
151 styler
. ColourTo ( endPos
, SCE_TCMD_WORD
);
155 // Check for Hide Command (@ECHO OFF/ON)
156 if ( lineBuffer
[ offset
] == '@' ) {
157 styler
. ColourTo ( startLine
+ offset
, SCE_TCMD_HIDE
);
161 while (( offset
< lengthLine
) && ( isspacechar ( lineBuffer
[ offset
]))) {
165 // Read remainder of line word-at-a-time or remainder-of-word-at-a-time
166 while ( offset
< lengthLine
) {
167 if ( offset
> startLine
) {
168 // Colorize Default Text
169 styler
. ColourTo ( startLine
+ offset
- 1 , SCE_TCMD_DEFAULT
);
171 // Copy word from Line Buffer into Word Buffer
173 for (; offset
< lengthLine
&& ( wbl
< 260 ) && ! isspacechar ( lineBuffer
[ offset
]); wbl
++, offset
++) {
174 wordBuffer
[ wbl
] = static_cast < char >( tolower ( lineBuffer
[ offset
]));
176 wordBuffer
[ wbl
] = '\0' ;
179 // Check for Separator
180 if ( IsBSeparator ( wordBuffer
[ 0 ])) {
182 // Reset Offset to re-process remainder of word
184 // Colorize Default Text
185 styler
. ColourTo ( startLine
+ offset
- 1 , SCE_BAT_DEFAULT
);
187 if ( wordBuffer
[ 0 ] == '"' )
188 inString
= ! inString
;
190 // Check for Regular expression
191 } else if (( wordBuffer
[ 0 ] == ':' ) && ( wordBuffer
[ 1 ] == ':' ) && ( continueProcessing
)) {
193 // Colorize Regular exoressuin
194 styler
. ColourTo ( startLine
+ offset
- 1 , SCE_TCMD_DEFAULT
);
195 // No need to Reset Offset
197 // Check for Labels in text (... :label)
198 } else if ( wordBuffer
[ 0 ] == ':' && isspacechar ( lineBuffer
[ offset
- wbl
- 1 ])) {
199 // Colorize Default Text
200 styler
. ColourTo ( startLine
+ offset
- 1 - wbl
, SCE_TCMD_DEFAULT
);
202 styler
. ColourTo ( startLine
+ offset
- 1 , SCE_TCMD_CLABEL
);
203 // No need to Reset Offset
204 // Check for delayed expansion Variable (!x...!)
205 } else if ( isDelayedExpansion
&& wordBuffer
[ 0 ] == '!' ) {
206 // Colorize Default Text
207 styler
. ColourTo ( startLine
+ offset
- 1 - wbl
, SCE_TCMD_DEFAULT
);
209 // Search to end of word for second !
210 while (( wbo
< wbl
) && ( wordBuffer
[ wbo
] != '!' ) && (! IsBOperator ( wordBuffer
[ wbo
])) && (! IsBSeparator ( wordBuffer
[ wbo
]))) {
213 if ( wordBuffer
[ wbo
] == '!' ) {
215 // Colorize Environment Variable
216 styler
. ColourTo ( startLine
+ offset
- 1 - ( wbl
- wbo
), SCE_TCMD_EXPANSION
);
220 styler
. ColourTo ( startLine
+ offset
- 1 - ( wbl
- 1 ), SCE_TCMD_DEFAULT
);
223 // Reset Offset to re-process remainder of word
224 offset
-= ( wbl
- wbo
);
226 // Check for Regular Keyword in list
227 } else if (( keywords
. InList ( wordBuffer
)) && (! inString
) && ( continueProcessing
)) {
229 // ECHO, PATH, and PROMPT require no further Regular Keyword Checking
230 if (( CompareCaseInsensitive ( wordBuffer
, "echo" ) == 0 ) ||
231 ( CompareCaseInsensitive ( sKeywordBuffer
, "echos" ) == 0 ) ||
232 ( CompareCaseInsensitive ( sKeywordBuffer
, "echoerr" ) == 0 ) ||
233 ( CompareCaseInsensitive ( sKeywordBuffer
, "echoserr" ) == 0 ) ||
234 ( CompareCaseInsensitive ( wordBuffer
, "path" ) == 0 ) ||
235 ( CompareCaseInsensitive ( wordBuffer
, "prompt" ) == 0 )) {
236 continueProcessing
= false ;
239 // Colorize Regular keyword
240 styler
. ColourTo ( startLine
+ offset
- 1 , SCE_TCMD_WORD
);
241 // No need to Reset Offset
243 } else if (( wordBuffer
[ 0 ] != '%' ) && ( wordBuffer
[ 0 ] != '!' ) && (! IsBOperator ( wordBuffer
[ 0 ])) && (! inString
) && ( continueProcessing
)) {
245 // a few commands accept "illegal" syntax -- cd\, echo., etc.
246 sscanf ( wordBuffer
, "%[^.<>|&= \\ /]" , sKeywordBuffer
);
247 sKeywordFound
= false ;
249 if (( CompareCaseInsensitive ( sKeywordBuffer
, "echo" ) == 0 ) ||
250 ( CompareCaseInsensitive ( sKeywordBuffer
, "echos" ) == 0 ) ||
251 ( CompareCaseInsensitive ( sKeywordBuffer
, "echoerr" ) == 0 ) ||
252 ( CompareCaseInsensitive ( sKeywordBuffer
, "echoserr" ) == 0 ) ||
253 ( CompareCaseInsensitive ( sKeywordBuffer
, "cd" ) == 0 ) ||
254 ( CompareCaseInsensitive ( sKeywordBuffer
, "path" ) == 0 ) ||
255 ( CompareCaseInsensitive ( sKeywordBuffer
, "prompt" ) == 0 )) {
257 // no further Regular Keyword Checking
258 continueProcessing
= false ;
259 sKeywordFound
= true ;
260 wbo
= ( unsigned int ) strlen ( sKeywordBuffer
);
262 // Colorize Special Keyword as Regular Keyword
263 styler
. ColourTo ( startLine
+ offset
- 1 - ( wbl
- wbo
), SCE_TCMD_WORD
);
264 // Reset Offset to re-process remainder of word
265 offset
-= ( wbl
- wbo
);
268 // Check for Default Text
269 if (! sKeywordFound
) {
271 // Read up to %, Operator or Separator
272 while (( wbo
< wbl
) && ( wordBuffer
[ wbo
] != '%' ) && (! isDelayedExpansion
|| wordBuffer
[ wbo
] != '!' ) && (! IsBOperator ( wordBuffer
[ wbo
])) && (! IsBSeparator ( wordBuffer
[ wbo
]))) {
275 // Colorize Default Text
276 styler
. ColourTo ( startLine
+ offset
- 1 - ( wbl
- wbo
), SCE_TCMD_DEFAULT
);
277 // Reset Offset to re-process remainder of word
278 offset
-= ( wbl
- wbo
);
281 // Check for Argument (%n), Environment Variable (%x...%) or Local Variable (%%a)
282 } else if ( wordBuffer
[ 0 ] == '%' ) {
285 // Colorize Default Text
286 styler
. ColourTo ( startLine
+ offset
- 1 - wbl
, SCE_TCMD_DEFAULT
);
289 // check for %[nn] syntax
290 if ( wordBuffer
[ 1 ] == '[' ) {
292 while (( n
< wbl
) && ( wordBuffer
[ n
] != ']' )) {
295 if ( wordBuffer
[ n
] == ']' )
300 // Search to end of word for second % or to the first terminator (can be a long path)
301 while (( wbo
< wbl
) && ( wordBuffer
[ wbo
] != '%' ) && (! IsBOperator ( wordBuffer
[ wbo
])) && (! IsBSeparator ( wordBuffer
[ wbo
]))) {
305 // Check for Argument (%n) or (%*)
306 if ((( isdigit ( wordBuffer
[ 1 ])) || ( wordBuffer
[ 1 ] == '*' )) && ( wordBuffer
[ wbo
] != '%' )) {
307 while (( wordBuffer
[ n
] ) && ( strchr ( " %0 123456789*#$" , wordBuffer
[ n
] ) != NULL
))
311 styler
. ColourTo ( startLine
+ offset
- 1 - ( wbl
- n
), SCE_TCMD_IDENTIFIER
);
312 // Reset Offset to re-process remainder of word
315 // Check for Variable with modifiers (%~...)
316 } else if (( varlen
= GetBatchVarLen ( wordBuffer
)) != 0 ) {
319 styler
. ColourTo ( startLine
+ offset
- 1 - ( wbl
- varlen
), SCE_TCMD_IDENTIFIER
);
320 // Reset Offset to re-process remainder of word
321 offset
-= ( wbl
- varlen
);
323 // Check for Environment Variable (%x...%)
324 } else if (( wordBuffer
[ 1 ] ) && ( wordBuffer
[ 1 ] != '%' )) {
325 if ( wordBuffer
[ wbo
] == '%' )
328 // Colorize Environment Variable
329 styler
. ColourTo ( startLine
+ offset
- 1 - ( wbl
- wbo
), SCE_TCMD_ENVIRONMENT
);
330 // Reset Offset to re-process remainder of word
331 offset
-= ( wbl
- wbo
);
333 // Check for Local Variable (%%a)
334 } else if ( ( wbl
> 2 ) && ( wordBuffer
[ 1 ] == '%' ) && ( wordBuffer
[ 2 ] != '%' ) && (! IsBOperator ( wordBuffer
[ 2 ])) && (! IsBSeparator ( wordBuffer
[ 2 ]))) {
337 while (( wordBuffer
[ n
] ) && (! IsBOperator ( wordBuffer
[ n
])) && (! IsBSeparator ( wordBuffer
[ n
])))
340 // Colorize Local Variable
341 styler
. ColourTo ( startLine
+ offset
- 1 - ( wbl
- n
), SCE_TCMD_IDENTIFIER
);
342 // Reset Offset to re-process remainder of word
346 } else if (( wbl
> 1 ) && ( wordBuffer
[ 1 ] == '%' )) {
349 styler
. ColourTo ( startLine
+ offset
- 1 - ( wbl
- 2 ), SCE_TCMD_DEFAULT
);
350 // Reset Offset to re-process remainder of word
355 styler
. ColourTo ( startLine
+ offset
- 1 - ( wbl
- 1 ), SCE_TCMD_DEFAULT
);
356 // Reset Offset to re-process remainder of word
360 // Check for Operator
361 } else if ( IsBOperator ( wordBuffer
[ 0 ])) {
362 // Colorize Default Text
363 styler
. ColourTo ( startLine
+ offset
- 1 - wbl
, SCE_TCMD_DEFAULT
);
365 // Check for Pipe, compound, or conditional Operator
366 if (( wordBuffer
[ 0 ] == '|' ) || ( wordBuffer
[ 0 ] == '&' )) {
368 // Colorize Pipe Operator
369 styler
. ColourTo ( startLine
+ offset
- 1 - ( wbl
- 1 ), SCE_TCMD_OPERATOR
);
370 // Reset Offset to re-process remainder of word
372 continueProcessing
= true ;
374 // Check for Other Operator
376 // Check for > Operator
377 if (( wordBuffer
[ 0 ] == '>' ) || ( wordBuffer
[ 0 ] == '<' )) {
378 // Turn Keyword and External Command / Program checking back on
379 continueProcessing
= true ;
381 // Colorize Other Operator
382 if (! inString
|| !( wordBuffer
[ 0 ] == '(' || wordBuffer
[ 0 ] == ')' ))
383 styler
. ColourTo ( startLine
+ offset
- 1 - ( wbl
- 1 ), SCE_TCMD_OPERATOR
);
384 // Reset Offset to re-process remainder of word
388 // Check for Default Text
390 // Read up to %, Operator or Separator
391 while (( wbo
< wbl
) && ( wordBuffer
[ wbo
] != '%' ) && (! isDelayedExpansion
|| wordBuffer
[ wbo
] != '!' ) && (! IsBOperator ( wordBuffer
[ wbo
])) && (! IsBSeparator ( wordBuffer
[ wbo
]))) {
394 // Colorize Default Text
395 styler
. ColourTo ( startLine
+ offset
- 1 - ( wbl
- wbo
), SCE_TCMD_DEFAULT
);
396 // Reset Offset to re-process remainder of word
397 offset
-= ( wbl
- wbo
);
400 // Skip whitespace - nothing happens if Offset was Reset
401 while (( offset
< lengthLine
) && ( isspacechar ( lineBuffer
[ offset
]))) {
405 // Colorize Default Text for remainder of line - currently not lexed
406 styler
. ColourTo ( endPos
, SCE_TCMD_DEFAULT
);
409 static void ColouriseTCMDDoc ( unsigned int startPos
, int length
, int /*initStyle*/ , WordList
* keywordlists
[], Accessor
& styler
)
411 char lineBuffer
[ 16384 ];
413 styler
. StartAt ( startPos
);
414 styler
. StartSegment ( startPos
);
415 unsigned int linePos
= 0 ;
416 unsigned int startLine
= startPos
;
417 for ( unsigned int i
= startPos
; i
< startPos
+ length
; i
++) {
418 lineBuffer
[ linePos
++] = styler
[ i
];
419 if ( AtEOL ( styler
, i
) || ( linePos
>= sizeof ( lineBuffer
) - 1 )) {
420 // End of line (or of line buffer) met, colourise it
421 lineBuffer
[ linePos
] = '\0' ;
422 ColouriseTCMDLine ( lineBuffer
, linePos
, startLine
, i
, keywordlists
, styler
);
427 if ( linePos
> 0 ) { // Last line does not have ending characters
428 lineBuffer
[ linePos
] = '\0' ;
429 ColouriseTCMDLine ( lineBuffer
, linePos
, startLine
, startPos
+ length
- 1 , keywordlists
, styler
);
433 // Convert string to upper case
434 static void StrUpr ( char * s
) {
436 * s
= MakeUpperCase (* s
);
441 // Folding support (for DO, IFF, SWITCH, TEXT, and command groups)
442 static void FoldTCMDDoc ( unsigned int startPos
, int length
, int , WordList
*[], Accessor
& styler
)
444 int line
= styler
. GetLine ( startPos
);
445 int level
= styler
. LevelAt ( line
);
447 unsigned int endPos
= startPos
+ length
;
450 char chPrev
= styler
. SafeGetCharAt ( startPos
- 1 );
453 for ( unsigned int i
= startPos
; i
< endPos
; i
++) {
455 int c
= styler
. SafeGetCharAt ( i
, ' \n ' );
456 int style
= styler
. StyleAt ( i
);
457 bool bLineStart
= (( chPrev
== ' \r ' ) || ( chPrev
== ' \n ' )) || i
== 0 ;
459 if ( style
== SCE_TCMD_OPERATOR
) {
463 } else if ( c
== ')' ) {
468 if (( bLineStart
) && ( style
== SCE_TCMD_WORD
)) {
469 for ( unsigned int j
= 0 ; j
< 10 ; j
++) {
470 if (! iswordchar ( styler
[ i
+ j
])) {
473 s
[ j
] = styler
[ i
+ j
];
478 if (( strcmp ( s
, "DO" ) == 0 ) || ( strcmp ( s
, "IFF" ) == 0 ) || ( strcmp ( s
, "SWITCH" ) == 0 ) || ( strcmp ( s
, "TEXT" ) == 0 )) {
480 } else if (( strcmp ( s
, "ENDDO" ) == 0 ) || ( strcmp ( s
, "ENDIFF" ) == 0 ) || ( strcmp ( s
, "ENDSWITCH" ) == 0 ) || ( strcmp ( s
, "ENDTEXT" ) == 0 )) {
485 if ( c
== ' \n ' ) { // line end
486 if ( levelIndent
> 0 ) {
487 level
|= SC_FOLDLEVELHEADERFLAG
;
489 if ( level
!= styler
. LevelAt ( line
))
490 styler
. SetLevel ( line
, level
);
491 level
+= levelIndent
;
492 if (( level
& SC_FOLDLEVELNUMBERMASK
) < SC_FOLDLEVELBASE
)
493 level
= SC_FOLDLEVELBASE
;
497 level
&= ~ SC_FOLDLEVELHEADERFLAG
;
498 level
&= ~ SC_FOLDLEVELWHITEFLAG
;
505 static const char * const tcmdWordListDesc
[] = {
511 LexerModule
lmTCMD ( SCLEX_TCMD
, ColouriseTCMDDoc
, "tcmd" , FoldTCMDDoc
, tcmdWordListDesc
);