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
29using namespace Lexilla;
30
31namespace {
32
33constexpr bool Is0To9(char ch) noexcept {
34 return (ch >= '0') && (ch <= '9');
35}
36
37bool IsAlphabetic(int ch) noexcept {
38 return IsASCII(ch) && isalpha(ch);
39}
40
41inline 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
47constexpr 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
54constexpr bool IsBSeparator(char ch) noexcept {
55 return (ch == '\\') || (ch == '.') || (ch == ';') ||
56 (ch == '\"') || (ch == '\'') || (ch == '/');
57}
58
59// Tests for escape character
60bool 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
73bool 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
90void 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
619const char *const batchWordListDesc[] = {
620 "Internal Commands",
621 "External Commands",
622 0
623};
624
625}
626
627LexerModule lmBatch(SCLEX_BATCH, ColouriseBatchDoc, "batch", 0, batchWordListDesc);
628