2
2
/** @file LexBash.cxx
5
// Copyright 2004-2010 by Neil Hodgson <neilh@scintilla.org>
5
// Copyright 2004-2012 by Neil Hodgson <neilh@scintilla.org>
6
6
// Adapted from LexPerl by Kein-Hong Man 2004
7
7
// The License.txt file describes the conditions under which this software may be distributed.
49
49
#define BASH_CMD_ARITH 4
50
50
#define BASH_CMD_DELIM 5
52
// state constants for nested delimiter pairs, used by
53
// SCE_SH_STRING and SCE_SH_BACKTICKS processing
54
#define BASH_DELIM_LITERAL 0
55
#define BASH_DELIM_STRING 1
56
#define BASH_DELIM_CSTRING 2
57
#define BASH_DELIM_LSTRING 3
58
#define BASH_DELIM_COMMAND 4
59
#define BASH_DELIM_BACKTICK 5
61
#define BASH_DELIM_STACK_MAX 7
52
63
static inline int translateBashDigit(int ch) {
53
64
if (ch >= '0' && ch <= '9') {
168
class QuoteStackCls { // Class to manage quote pairs that nest
173
int Depth; // levels pushed
183
CountStack = new int[BASH_DELIM_STACK_MAX];
184
UpStack = new int[BASH_DELIM_STACK_MAX];
185
StyleStack = new int[BASH_DELIM_STACK_MAX];
187
void Start(int u, int s) {
193
void Push(int u, int s) {
194
if (Depth >= BASH_DELIM_STACK_MAX)
196
CountStack[Depth] = Count;
197
UpStack [Depth] = Up;
198
StyleStack[Depth] = Style;
209
Count = CountStack[Depth];
210
Up = UpStack [Depth];
211
Style = StyleStack[Depth];
220
QuoteStackCls QuoteStack;
159
224
unsigned int endPos = startPos + length;
163
228
// Always backtracks to the start of a line that is not a continuation
164
229
// of the previous line (i.e. start of a bash command segment)
165
230
int ln = styler.GetLine(startPos);
231
if (ln > 0 && startPos == static_cast<unsigned int>(styler.LineStart(ln)))
167
234
startPos = styler.LineStart(ln);
168
235
if (ln == 0 || styler.GetLineState(ln) == BASH_CMD_START)
376
443
sc.ForwardSetState(SCE_SH_DEFAULT);
377
444
} else if (sc.ch == '\\') {
378
445
// skip escape prefix
446
} else if (!HereDoc.Quoted) {
380
447
sc.SetState(SCE_SH_DEFAULT);
382
449
if (HereDoc.DelimiterLength >= HERE_DELIM_MAX - 1) { // force blowup
402
469
char s[HERE_DELIM_MAX];
403
470
sc.GetCurrent(s, sizeof(s));
404
if (sc.LengthCurrent() == 0)
471
if (sc.LengthCurrent() == 0) { // '' or "" delimiters
472
if (prefixws == 0 && HereDoc.Quoted && HereDoc.DelimiterLength == 0)
473
sc.SetState(SCE_SH_DEFAULT);
406
476
if (s[strlen(s) - 1] == '\r')
407
477
s[strlen(s) - 1] = '\0';
408
478
if (strcmp(HereDoc.Delimiter, s) == 0) {
427
case SCE_SH_STRING: // delimited styles
428
case SCE_SH_CHARACTER:
497
case SCE_SH_STRING: // delimited styles, can nest
429
498
case SCE_SH_BACKTICKS:
499
if (sc.ch == '\\' && QuoteStack.Up != '\\') {
500
if (QuoteStack.Style != BASH_DELIM_LITERAL)
502
} else if (sc.ch == QuoteStack.Down) {
504
if (QuoteStack.Count == 0) {
505
if (QuoteStack.Depth > 0) {
508
sc.ForwardSetState(SCE_SH_DEFAULT);
510
} else if (sc.ch == QuoteStack.Up) {
513
if (QuoteStack.Style == BASH_DELIM_STRING ||
514
QuoteStack.Style == BASH_DELIM_LSTRING
515
) { // do nesting for "string", $"locale-string"
517
QuoteStack.Push(sc.ch, BASH_DELIM_BACKTICK);
518
} else if (sc.ch == '$' && sc.chNext == '(') {
520
QuoteStack.Push(sc.ch, BASH_DELIM_COMMAND);
522
} else if (QuoteStack.Style == BASH_DELIM_COMMAND ||
523
QuoteStack.Style == BASH_DELIM_BACKTICK
524
) { // do nesting for $(command), `command`
526
QuoteStack.Push(sc.ch, BASH_DELIM_LITERAL);
527
} else if (sc.ch == '\"') {
528
QuoteStack.Push(sc.ch, BASH_DELIM_STRING);
529
} else if (sc.ch == '`') {
530
QuoteStack.Push(sc.ch, BASH_DELIM_BACKTICK);
531
} else if (sc.ch == '$') {
532
if (sc.chNext == '\'') {
534
QuoteStack.Push(sc.ch, BASH_DELIM_CSTRING);
535
} else if (sc.chNext == '\"') {
537
QuoteStack.Push(sc.ch, BASH_DELIM_LSTRING);
538
} else if (sc.chNext == '(') {
540
QuoteStack.Push(sc.ch, BASH_DELIM_COMMAND);
546
case SCE_SH_PARAM: // ${parameter}
431
547
if (sc.ch == '\\' && Quote.Up != '\\') {
433
549
} else if (sc.ch == Quote.Down) {
454
578
sc.ChangeState(SCE_SH_ERROR);
456
580
// HereDoc.Quote always == '\''
581
sc.SetState(SCE_SH_HERE_Q);
582
} else if (HereDoc.DelimiterLength == 0) {
583
// no delimiter, illegal (but '' and "" are legal)
584
sc.ChangeState(SCE_SH_ERROR);
585
sc.SetState(SCE_SH_DEFAULT);
587
sc.SetState(SCE_SH_HERE_Q);
458
sc.SetState(SCE_SH_HERE_Q);
461
591
// update cmdState about the current command segment
490
620
sc.SetState(SCE_SH_COMMENTLINE);
491
621
} else if (sc.ch == '\"') {
492
622
sc.SetState(SCE_SH_STRING);
623
QuoteStack.Start(sc.ch, BASH_DELIM_STRING);
494
624
} else if (sc.ch == '\'') {
495
625
sc.SetState(SCE_SH_CHARACTER);
496
626
Quote.Start(sc.ch);
497
627
} else if (sc.ch == '`') {
498
628
sc.SetState(SCE_SH_BACKTICKS);
629
QuoteStack.Start(sc.ch, BASH_DELIM_BACKTICK);
500
630
} else if (sc.ch == '$') {
501
631
if (sc.Match("$((")) {
502
632
sc.SetState(SCE_SH_OPERATOR); // handle '((' later
507
637
if (sc.ch == '{') {
508
638
sc.ChangeState(SCE_SH_PARAM);
509
640
} else if (sc.ch == '\'') {
510
sc.ChangeState(SCE_SH_CHARACTER);
641
sc.ChangeState(SCE_SH_STRING);
642
QuoteStack.Start(sc.ch, BASH_DELIM_CSTRING);
511
643
} else if (sc.ch == '"') {
512
644
sc.ChangeState(SCE_SH_STRING);
513
} else if (sc.ch == '(' || sc.ch == '`') {
514
sc.ChangeState(SCE_SH_BACKTICKS);
645
QuoteStack.Start(sc.ch, BASH_DELIM_LSTRING);
646
} else if (sc.ch == '(') {
647
sc.ChangeState(SCE_SH_BACKTICKS);
648
QuoteStack.Start(sc.ch, BASH_DELIM_COMMAND);
649
} else if (sc.ch == '`') { // $` seen in a configure script, valid?
650
sc.ChangeState(SCE_SH_BACKTICKS);
651
QuoteStack.Start(sc.ch, BASH_DELIM_BACKTICK);
516
653
continue; // scalar has no delimiter pair
518
// fallthrough, open delim for $[{'"(`]
520
655
} else if (sc.Match('<', '<')) {
521
656
sc.SetState(SCE_SH_HERE_DELIM);
522
657
HereDoc.State = 0;