1 | // Scintilla source code edit control |
2 | /** @file LexBatch.cxx |
3 | ** Lexer for batch files. |
4 | **/ |
5 | // Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org> |
6 | // The License.txt file describes the conditions under which this software may be distributed. |
7 | |
8 | #include <stdlib.h> |
9 | #include <string.h> |
10 | #include <stdio.h> |
11 | #include <stdarg.h> |
12 | #include <assert.h> |
13 | #include <ctype.h> |
14 | |
15 | #include <string> |
16 | #include <string_view> |
17 | |
18 | #include "ILexer.h" |
19 | #include "Scintilla.h" |
20 | #include "SciLexer.h" |
21 | |
22 | #include "WordList.h" |
23 | #include "LexAccessor.h" |
24 | #include "Accessor.h" |
25 | #include "StyleContext.h" |
26 | #include "CharacterSet.h" |
27 | #include "LexerModule.h" |
28 | |
29 | using namespace Lexilla; |
30 | |
31 | namespace { |
32 | |
33 | constexpr bool Is0To9(char ch) noexcept { |
34 | return (ch >= '0') && (ch <= '9'); |
35 | } |
36 | |
37 | bool IsAlphabetic(int ch) noexcept { |
38 | return IsASCII(ch) && isalpha(ch); |
39 | } |
40 | |
41 | inline bool AtEOL(Accessor &styler, Sci_PositionU i) { |
42 | return (styler[i] == '\n') || |
43 | ((styler[i] == '\r') && (styler.SafeGetCharAt(i + 1) != '\n')); |
44 | } |
45 | |
46 | // Tests for BATCH Operators |
47 | constexpr bool IsBOperator(char ch) noexcept { |
48 | return (ch == '=') || (ch == '+') || (ch == '>') || (ch == '<') || |
49 | (ch == '|') || (ch == '?') || (ch == '*')|| |
50 | (ch == '&') || (ch == '(') || (ch == ')'); |
51 | } |
52 | |
53 | // Tests for BATCH Separators |
54 | constexpr bool IsBSeparator(char ch) noexcept { |
55 | return (ch == '\\') || (ch == '.') || (ch == ';') || |
56 | (ch == '\"') || (ch == '\'') || (ch == '/'); |
57 | } |
58 | |
59 | // Tests for escape character |
60 | bool IsEscaped(const char* wordStr, Sci_PositionU pos) noexcept { |
61 | bool isQoted=false; |
62 | while (pos>0){ |
63 | pos--; |
64 | if (wordStr[pos]=='^') |
65 | isQoted=!isQoted; |
66 | else |
67 | break; |
68 | } |
69 | return isQoted; |
70 | } |
71 | |
72 | // Tests for quote character |
73 | bool textQuoted(const char *lineBuffer, Sci_PositionU endPos) { |
74 | char strBuffer[1024]; |
75 | strncpy(strBuffer, lineBuffer, endPos); |
76 | strBuffer[endPos] = '\0'; |
77 | char *pQuote; |
78 | pQuote = strchr(strBuffer, '"'); |
79 | bool CurrentStatus = false; |
80 | while (pQuote != NULL) |
81 | { |
82 | if (!IsEscaped(strBuffer, pQuote - strBuffer)) { |
83 | CurrentStatus = !CurrentStatus; |
84 | } |
85 | pQuote = strchr(pQuote + 1, '"'); |
86 | } |
87 | return CurrentStatus; |
88 | } |
89 | |
90 | void ColouriseBatchDoc( |
91 | Sci_PositionU startPos, |
92 | Sci_Position length, |
93 | int /*initStyle*/, |
94 | WordList *keywordlists[], |
95 | Accessor &styler) { |
96 | // Always backtracks to the start of a line that is not a continuation |
97 | // of the previous line |
98 | if (startPos > 0) { |
99 | Sci_Position ln = styler.GetLine(startPos); // Current line number |
100 | while (startPos > 0) { |
101 | ln--; |
102 | if ((styler.SafeGetCharAt(startPos-3) == '^' && styler.SafeGetCharAt(startPos-2) == '\r' && styler.SafeGetCharAt(startPos-1) == '\n') |
103 | || styler.SafeGetCharAt(startPos-2) == '^') { // handle '^' line continuation |
104 | // When the line continuation is found, |
105 | // set the Start Position to the Start of the previous line |
106 | length+=startPos-styler.LineStart(ln); |
107 | startPos=styler.LineStart(ln); |
108 | } |
109 | else |
110 | break; |
111 | } |
112 | } |
113 | |
114 | char lineBuffer[1024] {}; |
115 | |
116 | styler.StartAt(startPos); |
117 | styler.StartSegment(startPos); |
118 | Sci_PositionU linePos = 0; |
119 | Sci_PositionU startLine = startPos; |
120 | bool continueProcessing = true; // Used to toggle Regular Keyword Checking |
121 | bool isNotAssigned=false; // Used to flag Assignment in Set operation |
122 | |
123 | for (Sci_PositionU i = startPos; i < startPos + length; i++) { |
124 | lineBuffer[linePos++] = styler[i]; |
125 | if (AtEOL(styler, i) || (linePos >= sizeof(lineBuffer) - 1) || (i==startPos + length-1)) { |
126 | // End of line (or of line buffer) (or End of Last Line) met, colourise it |
127 | lineBuffer[linePos] = '\0'; |
128 | const Sci_PositionU lengthLine=linePos; |
129 | const Sci_PositionU endPos=i; |
130 | const WordList &keywords = *keywordlists[0]; // Internal Commands |
131 | const WordList &keywords2 = *keywordlists[1]; // External Commands (optional) |
132 | |
133 | // CHOICE, ECHO, GOTO, PROMPT and SET have Default Text that may contain Regular Keywords |
134 | // Toggling Regular Keyword Checking off improves readability |
135 | // Other Regular Keywords and External Commands / Programs might also benefit from toggling |
136 | // Need a more robust algorithm to properly toggle Regular Keyword Checking |
137 | bool stopLineProcessing=false; // Used to stop line processing if Comment or Drive Change found |
138 | |
139 | Sci_PositionU offset = 0; // Line Buffer Offset |
140 | // Skip initial spaces |
141 | while ((offset < lengthLine) && (isspacechar(lineBuffer[offset]))) { |
142 | offset++; |
143 | } |
144 | // Colorize Default Text |
145 | styler.ColourTo(startLine + offset - 1, SCE_BAT_DEFAULT); |
146 | // Set External Command / Program Location |
147 | Sci_PositionU cmdLoc = offset; |
148 | |
149 | // Check for Fake Label (Comment) or Real Label - return if found |
150 | if (lineBuffer[offset] == ':') { |
151 | if (lineBuffer[offset + 1] == ':') { |
152 | // Colorize Fake Label (Comment) - :: is similar to REM, see http://content.techweb.com/winmag/columns/explorer/2000/21.htm |
153 | styler.ColourTo(endPos, SCE_BAT_COMMENT); |
154 | } else { |
155 | // Colorize Real Label |
156 | styler.ColourTo(endPos, SCE_BAT_LABEL); |
157 | } |
158 | stopLineProcessing=true; |
159 | // Check for Drive Change (Drive Change is internal command) - return if found |
160 | } else if ((IsAlphabetic(lineBuffer[offset])) && |
161 | (lineBuffer[offset + 1] == ':') && |
162 | ((isspacechar(lineBuffer[offset + 2])) || |
163 | (((lineBuffer[offset + 2] == '\\')) && |
164 | (isspacechar(lineBuffer[offset + 3]))))) { |
165 | // Colorize Regular Keyword |
166 | styler.ColourTo(endPos, SCE_BAT_WORD); |
167 | stopLineProcessing=true; |
168 | } |
169 | |
170 | // Check for Hide Command (@ECHO OFF/ON) |
171 | if (lineBuffer[offset] == '@') { |
172 | styler.ColourTo(startLine + offset, SCE_BAT_HIDE); |
173 | offset++; |
174 | } |
175 | // Skip next spaces |
176 | while ((offset < lengthLine) && (isspacechar(lineBuffer[offset]))) { |
177 | offset++; |
178 | } |
179 | |
180 | // Read remainder of line word-at-a-time or remainder-of-word-at-a-time |
181 | while (offset < lengthLine && !stopLineProcessing) { |
182 | if (offset > startLine) { |
183 | // Colorize Default Text |
184 | styler.ColourTo(startLine + offset - 1, SCE_BAT_DEFAULT); |
185 | } |
186 | char wordBuffer[81]{}; // Word Buffer - large to catch long paths |
187 | // Copy word from Line Buffer into Word Buffer |
188 | Sci_PositionU wbl = 0; // Word Buffer Length |
189 | for (; offset < lengthLine && wbl < 80 && |
190 | !isspacechar(lineBuffer[offset]); wbl++, offset++) { |
191 | wordBuffer[wbl] = tolower(lineBuffer[offset]); |
192 | } |
193 | wordBuffer[wbl] = '\0'; |
194 | Sci_PositionU wbo = 0; // Word Buffer Offset - also Special Keyword Buffer Length |
195 | |
196 | // Check for Comment - return if found |
197 | if ((CompareCaseInsensitive(wordBuffer, "rem" ) == 0) && continueProcessing) { |
198 | styler.ColourTo(endPos, SCE_BAT_COMMENT); |
199 | break; |
200 | } |
201 | // Check for Separator |
202 | if (IsBSeparator(wordBuffer[0])) { |
203 | // Check for External Command / Program |
204 | if ((cmdLoc == offset - wbl) && |
205 | ((wordBuffer[0] == ':') || |
206 | (wordBuffer[0] == '\\') || |
207 | (wordBuffer[0] == '.'))) { |
208 | // Reset Offset to re-process remainder of word |
209 | offset -= (wbl - 1); |
210 | // Colorize External Command / Program |
211 | if (!keywords2) { |
212 | styler.ColourTo(startLine + offset - 1, SCE_BAT_COMMAND); |
213 | } else if (keywords2.InList(wordBuffer)) { |
214 | styler.ColourTo(startLine + offset - 1, SCE_BAT_COMMAND); |
215 | } else { |
216 | styler.ColourTo(startLine + offset - 1, SCE_BAT_DEFAULT); |
217 | } |
218 | // Reset External Command / Program Location |
219 | cmdLoc = offset; |
220 | } else { |
221 | // Reset Offset to re-process remainder of word |
222 | offset -= (wbl - 1); |
223 | // Colorize Default Text |
224 | styler.ColourTo(startLine + offset - 1, SCE_BAT_DEFAULT); |
225 | } |
226 | // Check for Regular Keyword in list |
227 | } else if ((keywords.InList(wordBuffer)) && |
228 | (continueProcessing)) { |
229 | // ECHO, GOTO, PROMPT and SET require no further Regular Keyword Checking |
230 | if ((CompareCaseInsensitive(wordBuffer, "echo" ) == 0) || |
231 | (CompareCaseInsensitive(wordBuffer, "goto" ) == 0) || |
232 | (CompareCaseInsensitive(wordBuffer, "prompt" ) == 0)) { |
233 | continueProcessing = false; |
234 | } |
235 | // SET requires additional processing for the assignment operator |
236 | if (CompareCaseInsensitive(wordBuffer, "set" ) == 0) { |
237 | continueProcessing = false; |
238 | isNotAssigned=true; |
239 | } |
240 | // Identify External Command / Program Location for ERRORLEVEL, and EXIST |
241 | if ((CompareCaseInsensitive(wordBuffer, "errorlevel" ) == 0) || |
242 | (CompareCaseInsensitive(wordBuffer, "exist" ) == 0)) { |
243 | // Reset External Command / Program Location |
244 | cmdLoc = offset; |
245 | // Skip next spaces |
246 | while ((cmdLoc < lengthLine) && |
247 | (isspacechar(lineBuffer[cmdLoc]))) { |
248 | cmdLoc++; |
249 | } |
250 | // Skip comparison |
251 | while ((cmdLoc < lengthLine) && |
252 | (!isspacechar(lineBuffer[cmdLoc]))) { |
253 | cmdLoc++; |
254 | } |
255 | // Skip next spaces |
256 | while ((cmdLoc < lengthLine) && |
257 | (isspacechar(lineBuffer[cmdLoc]))) { |
258 | cmdLoc++; |
259 | } |
260 | // Identify External Command / Program Location for CALL, DO, LOADHIGH and LH |
261 | } else if ((CompareCaseInsensitive(wordBuffer, "call" ) == 0) || |
262 | (CompareCaseInsensitive(wordBuffer, "do" ) == 0) || |
263 | (CompareCaseInsensitive(wordBuffer, "loadhigh" ) == 0) || |
264 | (CompareCaseInsensitive(wordBuffer, "lh" ) == 0)) { |
265 | // Reset External Command / Program Location |
266 | cmdLoc = offset; |
267 | // Skip next spaces |
268 | while ((cmdLoc < lengthLine) && |
269 | (isspacechar(lineBuffer[cmdLoc]))) { |
270 | cmdLoc++; |
271 | } |
272 | } |
273 | // Colorize Regular keyword |
274 | styler.ColourTo(startLine + offset - 1, SCE_BAT_WORD); |
275 | // No need to Reset Offset |
276 | // Check for Special Keyword in list, External Command / Program, or Default Text |
277 | } else if ((wordBuffer[0] != '%') && |
278 | (wordBuffer[0] != '!') && |
279 | (!IsBOperator(wordBuffer[0])) && |
280 | (continueProcessing)) { |
281 | // Check for Special Keyword |
282 | // Affected Commands are in Length range 2-6 |
283 | // Good that ERRORLEVEL, EXIST, CALL, DO, LOADHIGH, and LH are unaffected |
284 | bool sKeywordFound = false; // Exit Special Keyword for-loop if found |
285 | for (Sci_PositionU keywordLength = 2; keywordLength < wbl && keywordLength < 7 && !sKeywordFound; keywordLength++) { |
286 | // Special Keywords are those that allow certain characters without whitespace after the command |
287 | // Examples are: cd. cd\ md. rd. dir| dir> echo: echo. path= |
288 | // Special Keyword Buffer used to determine if the first n characters is a Keyword |
289 | char sKeywordBuffer[10]{}; // Special Keyword Buffer |
290 | wbo = 0; |
291 | // Copy Keyword Length from Word Buffer into Special Keyword Buffer |
292 | for (; wbo < keywordLength; wbo++) { |
293 | sKeywordBuffer[wbo] = wordBuffer[wbo]; |
294 | } |
295 | sKeywordBuffer[wbo] = '\0'; |
296 | // Check for Special Keyword in list |
297 | if ((keywords.InList(sKeywordBuffer)) && |
298 | ((IsBOperator(wordBuffer[wbo])) || |
299 | (IsBSeparator(wordBuffer[wbo])))) { |
300 | sKeywordFound = true; |
301 | // ECHO requires no further Regular Keyword Checking |
302 | if (CompareCaseInsensitive(sKeywordBuffer, "echo" ) == 0) { |
303 | continueProcessing = false; |
304 | } |
305 | // Colorize Special Keyword as Regular Keyword |
306 | styler.ColourTo(startLine + offset - 1 - (wbl - wbo), SCE_BAT_WORD); |
307 | // Reset Offset to re-process remainder of word |
308 | offset -= (wbl - wbo); |
309 | } |
310 | } |
311 | // Check for External Command / Program or Default Text |
312 | if (!sKeywordFound) { |
313 | wbo = 0; |
314 | // Check for External Command / Program |
315 | if (cmdLoc == offset - wbl) { |
316 | // Read up to %, Operator or Separator |
317 | while ((wbo < wbl) && |
318 | (((wordBuffer[wbo] != '%') && |
319 | (wordBuffer[wbo] != '!') && |
320 | (!IsBOperator(wordBuffer[wbo])) && |
321 | (!IsBSeparator(wordBuffer[wbo]))))) { |
322 | wbo++; |
323 | } |
324 | // Reset External Command / Program Location |
325 | cmdLoc = offset - (wbl - wbo); |
326 | // Reset Offset to re-process remainder of word |
327 | offset -= (wbl - wbo); |
328 | // CHOICE requires no further Regular Keyword Checking |
329 | if (CompareCaseInsensitive(wordBuffer, "choice" ) == 0) { |
330 | continueProcessing = false; |
331 | } |
332 | // Check for START (and its switches) - What follows is External Command \ Program |
333 | if (CompareCaseInsensitive(wordBuffer, "start" ) == 0) { |
334 | // Reset External Command / Program Location |
335 | cmdLoc = offset; |
336 | // Skip next spaces |
337 | while ((cmdLoc < lengthLine) && |
338 | (isspacechar(lineBuffer[cmdLoc]))) { |
339 | cmdLoc++; |
340 | } |
341 | // Reset External Command / Program Location if command switch detected |
342 | if (lineBuffer[cmdLoc] == '/') { |
343 | // Skip command switch |
344 | while ((cmdLoc < lengthLine) && |
345 | (!isspacechar(lineBuffer[cmdLoc]))) { |
346 | cmdLoc++; |
347 | } |
348 | // Skip next spaces |
349 | while ((cmdLoc < lengthLine) && |
350 | (isspacechar(lineBuffer[cmdLoc]))) { |
351 | cmdLoc++; |
352 | } |
353 | } |
354 | } |
355 | // Colorize External Command / Program |
356 | if (!keywords2) { |
357 | styler.ColourTo(startLine + offset - 1, SCE_BAT_COMMAND); |
358 | } else if (keywords2.InList(wordBuffer)) { |
359 | styler.ColourTo(startLine + offset - 1, SCE_BAT_COMMAND); |
360 | } else { |
361 | styler.ColourTo(startLine + offset - 1, SCE_BAT_DEFAULT); |
362 | } |
363 | // No need to Reset Offset |
364 | // Check for Default Text |
365 | } else { |
366 | // Read up to %, Operator or Separator |
367 | while ((wbo < wbl) && |
368 | (((wordBuffer[wbo] != '%') && |
369 | (wordBuffer[wbo] != '!') && |
370 | (!IsBOperator(wordBuffer[wbo])) && |
371 | (!IsBSeparator(wordBuffer[wbo]))))) { |
372 | wbo++; |
373 | } |
374 | // Colorize Default Text |
375 | styler.ColourTo(startLine + offset - 1 - (wbl - wbo), SCE_BAT_DEFAULT); |
376 | // Reset Offset to re-process remainder of word |
377 | offset -= (wbl - wbo); |
378 | } |
379 | } |
380 | // Check for Argument (%n), Environment Variable (%x...%) or Local Variable (%%a) |
381 | } else if (wordBuffer[0] == '%') { |
382 | // Colorize Default Text |
383 | styler.ColourTo(startLine + offset - 1 - wbl, SCE_BAT_DEFAULT); |
384 | wbo++; |
385 | // Search to end of word for second % (can be a long path) |
386 | while ((wbo < wbl) && |
387 | (wordBuffer[wbo] != '%')) { |
388 | wbo++; |
389 | } |
390 | // Check for Argument (%n) or (%*) |
391 | if (((Is0To9(wordBuffer[1])) || (wordBuffer[1] == '*')) && |
392 | (wordBuffer[wbo] != '%')) { |
393 | // Check for External Command / Program |
394 | if (cmdLoc == offset - wbl) { |
395 | cmdLoc = offset - (wbl - 2); |
396 | } |
397 | // Colorize Argument |
398 | styler.ColourTo(startLine + offset - 1 - (wbl - 2), SCE_BAT_IDENTIFIER); |
399 | // Reset Offset to re-process remainder of word |
400 | offset -= (wbl - 2); |
401 | // Check for Expanded Argument (%~...) / Variable (%%~...) |
402 | // Expanded Argument: %~[<path-operators>]<single digit> |
403 | // Expanded Variable: %%~[<path-operators>]<single identifier character> |
404 | // Path operators are exclusively alphabetic. |
405 | // Expanded arguments have a single digit at the end. |
406 | // Expanded variables have a single identifier character as variable name. |
407 | } else if (((wbl > 1) && (wordBuffer[1] == '~')) || |
408 | ((wbl > 2) && (wordBuffer[1] == '%') && (wordBuffer[2] == '~'))) { |
409 | // Check for External Command / Program |
410 | if (cmdLoc == offset - wbl) { |
411 | cmdLoc = offset - (wbl - wbo); |
412 | } |
413 | const bool isArgument = (wordBuffer[1] == '~'); |
414 | if (isArgument) { |
415 | Sci_PositionU expansionStopOffset = 2; |
416 | bool isValid = false; |
417 | for (; expansionStopOffset < wbl; expansionStopOffset++) { |
418 | if (Is0To9(wordBuffer[expansionStopOffset])) { |
419 | expansionStopOffset++; |
420 | isValid = true; |
421 | wbo = expansionStopOffset; |
422 | // Colorize Expanded Argument |
423 | styler.ColourTo(startLine + offset - 1 - (wbl - wbo), SCE_BAT_IDENTIFIER); |
424 | break; |
425 | } |
426 | } |
427 | if (!isValid) { |
428 | // not a valid expanded argument or variable |
429 | styler.ColourTo(startLine + offset - 1 - (wbl - wbo), SCE_BAT_DEFAULT); |
430 | } |
431 | // Expanded Variable |
432 | } else { |
433 | // start after ~ |
434 | wbo = 3; |
435 | // Search to end of word for another % (can be a long path) |
436 | while ((wbo < wbl) && |
437 | (wordBuffer[wbo] != '%') && |
438 | (!IsBOperator(wordBuffer[wbo])) && |
439 | (!IsBSeparator(wordBuffer[wbo]))) { |
440 | wbo++; |
441 | } |
442 | if (wbo > 3) { |
443 | // Colorize Expanded Variable |
444 | styler.ColourTo(startLine + offset - 1 - (wbl - wbo), SCE_BAT_IDENTIFIER); |
445 | } else { |
446 | // not a valid expanded argument or variable |
447 | styler.ColourTo(startLine + offset - 1 - (wbl - wbo), SCE_BAT_DEFAULT); |
448 | } |
449 | } |
450 | // Reset Offset to re-process remainder of word |
451 | offset -= (wbl - wbo); |
452 | // Check for Environment Variable (%x...%) |
453 | } else if ((wordBuffer[1] != '%') && |
454 | (wordBuffer[wbo] == '%')) { |
455 | wbo++; |
456 | // Check for External Command / Program |
457 | if (cmdLoc == offset - wbl) { |
458 | cmdLoc = offset - (wbl - wbo); |
459 | } |
460 | // Colorize Environment Variable |
461 | styler.ColourTo(startLine + offset - 1 - (wbl - wbo), SCE_BAT_IDENTIFIER); |
462 | // Reset Offset to re-process remainder of word |
463 | offset -= (wbl - wbo); |
464 | // Check for Local Variable (%%a) |
465 | } else if ( |
466 | (wbl > 2) && |
467 | (wordBuffer[1] == '%') && |
468 | (wordBuffer[2] != '%') && |
469 | (!IsBOperator(wordBuffer[2])) && |
470 | (!IsBSeparator(wordBuffer[2]))) { |
471 | // Check for External Command / Program |
472 | if (cmdLoc == offset - wbl) { |
473 | cmdLoc = offset - (wbl - 3); |
474 | } |
475 | // Colorize Local Variable |
476 | styler.ColourTo(startLine + offset - 1 - (wbl - 3), SCE_BAT_IDENTIFIER); |
477 | // Reset Offset to re-process remainder of word |
478 | offset -= (wbl - 3); |
479 | // escaped % |
480 | } else if ( |
481 | (wbl > 1) && |
482 | (wordBuffer[1] == '%')) { |
483 | |
484 | // Reset Offset to re-process remainder of word |
485 | styler.ColourTo(startLine + offset - 1 - (wbl - 2), SCE_BAT_DEFAULT); |
486 | offset -= (wbl - 2); |
487 | } |
488 | // Check for Environment Variable (!x...!) |
489 | } else if (wordBuffer[0] == '!') { |
490 | // Colorize Default Text |
491 | styler.ColourTo(startLine + offset - 1 - wbl, SCE_BAT_DEFAULT); |
492 | wbo++; |
493 | // Search to end of word for second ! (can be a long path) |
494 | while ((wbo < wbl) && |
495 | (wordBuffer[wbo] != '!')) { |
496 | wbo++; |
497 | } |
498 | if (wordBuffer[wbo] == '!') { |
499 | wbo++; |
500 | // Check for External Command / Program |
501 | if (cmdLoc == offset - wbl) { |
502 | cmdLoc = offset - (wbl - wbo); |
503 | } |
504 | // Colorize Environment Variable |
505 | styler.ColourTo(startLine + offset - 1 - (wbl - wbo), SCE_BAT_IDENTIFIER); |
506 | // Reset Offset to re-process remainder of word |
507 | offset -= (wbl - wbo); |
508 | } |
509 | // Check for Operator |
510 | } else if (IsBOperator(wordBuffer[0])) { |
511 | // Colorize Default Text |
512 | styler.ColourTo(startLine + offset - 1 - wbl, SCE_BAT_DEFAULT); |
513 | // Check for Comparison Operator |
514 | if ((wordBuffer[0] == '=') && (wordBuffer[1] == '=')) { |
515 | // Identify External Command / Program Location for IF |
516 | cmdLoc = offset; |
517 | // Skip next spaces |
518 | while ((cmdLoc < lengthLine) && |
519 | (isspacechar(lineBuffer[cmdLoc]))) { |
520 | cmdLoc++; |
521 | } |
522 | // Colorize Comparison Operator |
523 | if (continueProcessing) |
524 | styler.ColourTo(startLine + offset - 1 - (wbl - 2), SCE_BAT_OPERATOR); |
525 | else |
526 | styler.ColourTo(startLine + offset - 1 - (wbl - 2), SCE_BAT_DEFAULT); |
527 | // Reset Offset to re-process remainder of word |
528 | offset -= (wbl - 2); |
529 | // Check for Pipe Operator |
530 | } else if ((wordBuffer[0] == '|') && |
531 | !(IsEscaped(lineBuffer,offset - wbl + wbo) || textQuoted(lineBuffer, offset - wbl) )) { |
532 | // Reset External Command / Program Location |
533 | cmdLoc = offset - wbl + 1; |
534 | // Skip next spaces |
535 | while ((cmdLoc < lengthLine) && |
536 | (isspacechar(lineBuffer[cmdLoc]))) { |
537 | cmdLoc++; |
538 | } |
539 | // Colorize Pipe Operator |
540 | styler.ColourTo(startLine + offset - 1 - (wbl - 1), SCE_BAT_OPERATOR); |
541 | // Reset Offset to re-process remainder of word |
542 | offset -= (wbl - 1); |
543 | continueProcessing = true; |
544 | // Check for Other Operator |
545 | } else { |
546 | // Check for Operators: >, |, & |
547 | if (((wordBuffer[0] == '>')|| |
548 | (wordBuffer[0] == ')')|| |
549 | (wordBuffer[0] == '(')|| |
550 | (wordBuffer[0] == '&' )) && |
551 | !(!continueProcessing && (IsEscaped(lineBuffer,offset - wbl + wbo) |
552 | || textQuoted(lineBuffer, offset - wbl) ))){ |
553 | // Turn Keyword and External Command / Program checking back on |
554 | continueProcessing = true; |
555 | isNotAssigned=false; |
556 | } |
557 | // Colorize Other Operators |
558 | // Do not Colorize Paranthesis, quoted text and escaped operators |
559 | if (((wordBuffer[0] != ')') && (wordBuffer[0] != '(') |
560 | && !textQuoted(lineBuffer, offset - wbl) && !IsEscaped(lineBuffer,offset - wbl + wbo)) |
561 | && !((wordBuffer[0] == '=') && !isNotAssigned )) |
562 | styler.ColourTo(startLine + offset - 1 - (wbl - 1), SCE_BAT_OPERATOR); |
563 | else |
564 | styler.ColourTo(startLine + offset - 1 - (wbl - 1), SCE_BAT_DEFAULT); |
565 | // Reset Offset to re-process remainder of word |
566 | offset -= (wbl - 1); |
567 | |
568 | if ((wordBuffer[0] == '=') && isNotAssigned ){ |
569 | isNotAssigned=false; |
570 | } |
571 | } |
572 | // Check for Default Text |
573 | } else { |
574 | // Read up to %, Operator or Separator |
575 | while ((wbo < wbl) && |
576 | ((wordBuffer[wbo] != '%') && |
577 | (wordBuffer[wbo] != '!') && |
578 | (!IsBOperator(wordBuffer[wbo])) && |
579 | (!IsBSeparator(wordBuffer[wbo])))) { |
580 | wbo++; |
581 | } |
582 | // Colorize Default Text |
583 | styler.ColourTo(startLine + offset - 1 - (wbl - wbo), SCE_BAT_DEFAULT); |
584 | // Reset Offset to re-process remainder of word |
585 | offset -= (wbl - wbo); |
586 | } |
587 | // Skip next spaces - nothing happens if Offset was Reset |
588 | while ((offset < lengthLine) && (isspacechar(lineBuffer[offset]))) { |
589 | offset++; |
590 | } |
591 | } |
592 | // Colorize Default Text for remainder of line - currently not lexed |
593 | styler.ColourTo(endPos, SCE_BAT_DEFAULT); |
594 | |
595 | // handle line continuation for SET and ECHO commands except the last line |
596 | if (!continueProcessing && (i<startPos + length-1)) { |
597 | if (linePos==1 || (linePos==2 && lineBuffer[1]=='\r')) // empty line on Unix and Mac or on Windows |
598 | continueProcessing=true; |
599 | else { |
600 | Sci_PositionU lineContinuationPos; |
601 | if ((linePos>2) && lineBuffer[linePos-2]=='\r') // Windows EOL |
602 | lineContinuationPos=linePos-3; |
603 | else |
604 | lineContinuationPos=linePos-2; // Unix or Mac EOL |
605 | // Reset continueProcessing if line continuation was not found |
606 | if ((lineBuffer[lineContinuationPos]!='^') |
607 | || IsEscaped(lineBuffer, lineContinuationPos) |
608 | || textQuoted(lineBuffer, lineContinuationPos)) |
609 | continueProcessing=true; |
610 | } |
611 | } |
612 | |
613 | linePos = 0; |
614 | startLine = i + 1; |
615 | } |
616 | } |
617 | } |
618 | |
619 | const char *const batchWordListDesc[] = { |
620 | "Internal Commands" , |
621 | "External Commands" , |
622 | 0 |
623 | }; |
624 | |
625 | } |
626 | |
627 | LexerModule lmBatch(SCLEX_BATCH, ColouriseBatchDoc, "batch" , 0, batchWordListDesc); |
628 | |