~ubuntu-branches/ubuntu/raring/codeblocks/raring-proposed

« back to all changes in this revision

Viewing changes to src/sdk/wxscintilla/src/scintilla/src/LexBash.cxx

  • Committer: Bazaar Package Importer
  • Author(s): Cosme Domínguez Díaz
  • Date: 2010-08-09 04:38:38 UTC
  • mfrom: (1.1.1 upstream)
  • mto: This revision was merged to the branch mainline in revision 4.
  • Revision ID: james.westby@ubuntu.com-20100809043838-a59ygguym4eg0jgw
Tags: 10.05-0ubuntu1
* New upstream release. Closes (LP: #322350)
 - Switch to dpkg-source 3.0 (quilt) format
 - Remove unneeded README.source
 - Add debian/get-source-orig script that removes all
   Windows prebuilt binaries
* Bump Standards-Version to 3.9.1
 - Stop shipping *.la files
* debian/control
 - Add cdbs package as Build-Depend
 - Add libbz2-dev and zlib1g-dev packages as
   Build-Depends (needed by libhelp_plugin.so)
 - Remove dpatch package of Build-Depends
 - Add codeblocks-contrib-debug package
 - Split architecture-independent files of codeblocks
   package in codeblocks-common package
* debian/rules
 - Switch to CDBS rules system
 - Add parallel build support
 - Add a call to debian/get-source-orig script
 - Use lzma compression (saves 23,5 MB of free space)
* debian/patches
 - Refresh 01_codeblocks_plugin_path
 - Add 02_no_Makefiles_in_debian_dir to remove any link
   in codeblocks build system to deleted Makefiles of debian directory
 - Drop 02_ftbfs_gcc44 and 03_ftbfs_glib221 (merged in upstream)
* debian/watch
 - Update to use the new host (berlios.de)

Show diffs side-by-side

added added

removed removed

Lines of Context:
2
2
/** @file LexBash.cxx
3
3
 ** Lexer for Bash.
4
4
 **/
5
 
// Copyright 2004-2005 by Neil Hodgson <neilh@scintilla.org>
6
 
// Adapted from LexPerl by Kein-Hong Man <mkh@pl.jaring.my> 2004
 
5
// Copyright 2004-2008 by Neil Hodgson <neilh@scintilla.org>
 
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.
8
8
 
9
9
#include <stdlib.h>
16
16
 
17
17
#include "PropSet.h"
18
18
#include "Accessor.h"
 
19
#include "StyleContext.h"
19
20
#include "KeyWords.h"
20
21
#include "Scintilla.h"
21
22
#include "SciLexer.h"
 
23
#include "CharacterSet.h"
 
24
 
 
25
#ifdef SCI_NAMESPACE
 
26
using namespace Scintilla;
 
27
#endif
 
28
 
 
29
#define HERE_DELIM_MAX 256
 
30
 
 
31
// define this if you want 'invalid octals' to be marked as errors
 
32
// usually, this is not a good idea, permissive lexing is better
 
33
#undef PEDANTIC_OCTAL
22
34
 
23
35
#define BASH_BASE_ERROR         65
24
36
#define BASH_BASE_DECIMAL       66
25
37
#define BASH_BASE_HEX           67
 
38
#ifdef PEDANTIC_OCTAL
26
39
#define BASH_BASE_OCTAL         68
27
40
#define BASH_BASE_OCTAL_ERROR   69
28
 
 
29
 
#define HERE_DELIM_MAX 256
30
 
 
31
 
static inline int translateBashDigit(char ch) {
 
41
#endif
 
42
 
 
43
static inline int translateBashDigit(int ch) {
32
44
        if (ch >= '0' && ch <= '9') {
33
45
                return ch - '0';
34
46
        } else if (ch >= 'a' && ch <= 'z') {
43
55
        return BASH_BASE_ERROR;
44
56
}
45
57
 
46
 
static inline bool isEOLChar(char ch) {
47
 
        return (ch == '\r') || (ch == '\n');
48
 
}
49
 
 
50
 
static bool isSingleCharOp(char ch) {
51
 
        char strCharSet[2];
52
 
        strCharSet[0] = ch;
53
 
        strCharSet[1] = '\0';
54
 
        return (NULL != strstr("rwxoRWXOezsfdlpSbctugkTBMACahGLNn", strCharSet));
55
 
}
56
 
 
57
 
static inline bool isBashOperator(char ch) {
58
 
        if (ch == '^' || ch == '&' || ch == '\\' || ch == '%' ||
59
 
                ch == '(' || ch == ')' || ch == '-' || ch == '+' ||
60
 
                ch == '=' || ch == '|' || ch == '{' || ch == '}' ||
61
 
                ch == '[' || ch == ']' || ch == ':' || ch == ';' ||
62
 
                ch == '>' || ch == ',' || ch == '/' || ch == '<' ||
63
 
                ch == '?' || ch == '!' || ch == '.' || ch == '~' ||
64
 
                ch == '@')
65
 
                return true;
66
 
        return false;
67
 
}
68
 
 
69
 
static int classifyWordBash(unsigned int start, unsigned int end, WordList &keywords, Accessor &styler) {
70
 
        char s[100];
71
 
        for (unsigned int i = 0; i < end - start + 1 && i < 30; i++) {
72
 
                s[i] = styler[start + i];
73
 
                s[i + 1] = '\0';
74
 
        }
75
 
        char chAttr = SCE_SH_IDENTIFIER;
76
 
        if (keywords.InList(s))
77
 
                chAttr = SCE_SH_WORD;
78
 
        styler.ColourTo(end, chAttr);
79
 
        return chAttr;
80
 
}
81
 
 
82
 
static inline int getBashNumberBase(unsigned int start, unsigned int end, Accessor &styler) {
 
58
static inline int getBashNumberBase(char *s) {
 
59
        int i = 0;
83
60
        int base = 0;
84
 
        for (unsigned int i = 0; i < end - start + 1 && i < 10; i++) {
85
 
                base = base * 10 + (styler[start + i] - '0');
 
61
        while (*s) {
 
62
                base = base * 10 + (*s++ - '0');
 
63
                i++;
86
64
        }
87
 
        if (base > 64 || (end - start) > 1) {
 
65
        if (base > 64 || i > 2) {
88
66
                return BASH_BASE_ERROR;
89
67
        }
90
68
        return base;
91
69
}
92
70
 
93
 
static inline bool isEndVar(char ch) {
94
 
        return !isalnum(ch) && ch != '$' && ch != '_';
95
 
}
96
 
 
97
 
static inline bool isNonQuote(char ch) {
98
 
        return isalnum(ch) || ch == '_';
99
 
}
100
 
 
101
 
static bool isMatch(Accessor &styler, int lengthDoc, int pos, const char *val) {
102
 
        if ((pos + static_cast<int>(strlen(val))) >= lengthDoc) {
103
 
                return false;
104
 
        }
105
 
        while (*val) {
106
 
                if (*val != styler[pos++]) {
107
 
                        return false;
108
 
                }
109
 
                val++;
110
 
        }
111
 
        return true;
112
 
}
113
 
 
114
 
static char opposite(char ch) {
115
 
        if (ch == '(')
116
 
                return ')';
117
 
        if (ch == '[')
118
 
                return ']';
119
 
        if (ch == '{')
120
 
                return '}';
121
 
        if (ch == '<')
122
 
                return '>';
 
71
static int opposite(int ch) {
 
72
        if (ch == '(') return ')';
 
73
        if (ch == '[') return ']';
 
74
        if (ch == '{') return '}';
 
75
        if (ch == '<') return '>';
123
76
        return ch;
124
77
}
125
78
 
126
79
static void ColouriseBashDoc(unsigned int startPos, int length, int initStyle,
127
 
                             WordList *keywordlists[], Accessor &styler) {
128
 
 
129
 
        // Lexer for bash often has to backtrack to start of current style to determine
130
 
        // which characters are being used as quotes, how deeply nested is the
131
 
        // start position and what the termination string is for here documents
 
80
                                                         WordList *keywordlists[], Accessor &styler) {
132
81
 
133
82
        WordList &keywords = *keywordlists[0];
134
83
 
135
 
        class HereDocCls {
 
84
        CharacterSet setWordStart(CharacterSet::setAlpha, "_");
 
85
        // note that [+-] are often parts of identifiers in shell scripts
 
86
        CharacterSet setWord(CharacterSet::setAlphaNum, "._+-");
 
87
        CharacterSet setBashOperator(CharacterSet::setNone, "^&\\%()-+=|{}[]:;>,*/<?!.~@");
 
88
        CharacterSet setSingleCharOp(CharacterSet::setNone, "rwxoRWXOezsfdlpSbctugkTBMACahGLNn");
 
89
        CharacterSet setParam(CharacterSet::setAlphaNum, "$_");
 
90
        CharacterSet setHereDoc(CharacterSet::setAlpha, "_\\-+!");
 
91
        CharacterSet setHereDoc2(CharacterSet::setAlphaNum, "_-+!");
 
92
        CharacterSet setLeftShift(CharacterSet::setDigits, "=$");
 
93
 
 
94
        class HereDocCls {      // Class to manage HERE document elements
136
95
        public:
137
96
                int State;              // 0: '<<' encountered
138
97
                // 1: collect the delimiter
139
98
                // 2: here doc text (lines after the delimiter)
140
 
                char Quote;             // the char after '<<'
 
99
                int Quote;              // the char after '<<'
141
100
                bool Quoted;            // true if Quote in ('\'','"','`')
142
101
                bool Indent;            // indented delimiter (for <<-)
143
102
                int DelimiterLength;    // strlen(Delimiter)
144
103
                char *Delimiter;        // the Delimiter, 256: sizeof PL_tokenbuf
145
104
                HereDocCls() {
146
105
                        State = 0;
147
 
            Quote = 0;
148
 
            Quoted = false;
149
 
            Indent = 0;
 
106
                        Quote = 0;
 
107
                        Quoted = false;
 
108
                        Indent = 0;
150
109
                        DelimiterLength = 0;
151
110
                        Delimiter = new char[HERE_DELIM_MAX];
152
111
                        Delimiter[0] = '\0';
153
112
                }
 
113
                void Append(int ch) {
 
114
                        Delimiter[DelimiterLength++] = static_cast<char>(ch);
 
115
                        Delimiter[DelimiterLength] = '\0';
 
116
                }
154
117
                ~HereDocCls() {
155
118
                        delete []Delimiter;
156
119
                }
157
120
        };
158
121
        HereDocCls HereDoc;
159
122
 
160
 
        class QuoteCls {
 
123
        class QuoteCls {        // Class to manage quote pairs (simplified vs LexPerl)
161
124
                public:
162
 
                int  Rep;
163
 
                int  Count;
164
 
                char Up;
165
 
                char Down;
 
125
                int Count;
 
126
                int Up, Down;
166
127
                QuoteCls() {
167
 
                        this->New(1);
168
 
                }
169
 
                void New(int r) {
170
 
                        Rep   = r;
171
128
                        Count = 0;
172
129
                        Up    = '\0';
173
130
                        Down  = '\0';
174
131
                }
175
 
                void Open(char u) {
 
132
                void Open(int u) {
176
133
                        Count++;
177
134
                        Up    = u;
178
135
                        Down  = opposite(Up);
179
136
                }
 
137
                void Start(int u) {
 
138
                        Count = 0;
 
139
                        Open(u);
 
140
                }
180
141
        };
181
142
        QuoteCls Quote;
182
143
 
183
 
        int state = initStyle;
184
144
        int numBase = 0;
185
 
        unsigned int lengthDoc = startPos + length;
 
145
        int digit;
 
146
        unsigned int endPos = startPos + length;
186
147
 
187
 
        // If in a long distance lexical state, seek to the beginning to find quote characters
 
148
        // Backtrack to beginning of style if required...
 
149
        // If in a long distance lexical state, backtrack to find quote characters
 
150
        if (initStyle == SCE_SH_HERE_Q) {
 
151
                while ((startPos > 1) && (styler.StyleAt(startPos) != SCE_SH_HERE_DELIM)) {
 
152
                        startPos--;
 
153
                }
 
154
                startPos = styler.LineStart(styler.GetLine(startPos));
 
155
                initStyle = styler.StyleAt(startPos - 1);
 
156
        }
188
157
        // Bash strings can be multi-line with embedded newlines, so backtrack.
189
158
        // Bash numbers have additional state during lexing, so backtrack too.
190
 
        if (state == SCE_SH_HERE_Q) {
191
 
                while ((startPos > 1) && (styler.StyleAt(startPos) != SCE_SH_HERE_DELIM)) {
192
 
                        startPos--;
193
 
                }
194
 
                startPos = styler.LineStart(styler.GetLine(startPos));
195
 
                state = styler.StyleAt(startPos - 1);
196
 
        }
197
 
        if (state == SCE_SH_STRING
198
 
         || state == SCE_SH_BACKTICKS
199
 
         || state == SCE_SH_CHARACTER
200
 
         || state == SCE_SH_NUMBER
201
 
         || state == SCE_SH_IDENTIFIER
202
 
         || state == SCE_SH_COMMENTLINE
203
 
        ) {
204
 
                while ((startPos > 1) && (styler.StyleAt(startPos - 1) == state)) {
205
 
                        startPos--;
206
 
                }
207
 
                state = SCE_SH_DEFAULT;
208
 
        }
209
 
 
210
 
        styler.StartAt(startPos);
211
 
        char chPrev = styler.SafeGetCharAt(startPos - 1);
212
 
        if (startPos == 0)
213
 
                chPrev = '\n';
214
 
        char chNext = styler[startPos];
215
 
        styler.StartSegment(startPos);
216
 
 
217
 
        for (unsigned int i = startPos; i < lengthDoc; i++) {
218
 
                char ch = chNext;
219
 
                // if the current character is not consumed due to the completion of an
220
 
                // earlier style, lexing can be restarted via a simple goto
221
 
        restartLexer:
222
 
                chNext = styler.SafeGetCharAt(i + 1);
223
 
                char chNext2 = styler.SafeGetCharAt(i + 2);
224
 
 
225
 
                if (styler.IsLeadByte(ch)) {
226
 
                        chNext = styler.SafeGetCharAt(i + 2);
227
 
                        chPrev = ' ';
228
 
                        i += 1;
229
 
                        continue;
230
 
                }
231
 
 
232
 
                if ((chPrev == '\r' && ch == '\n')) {   // skip on DOS/Windows
233
 
                        styler.ColourTo(i, state);
234
 
                        chPrev = ch;
235
 
                        continue;
236
 
                }
237
 
 
238
 
                if (HereDoc.State == 1 && isEOLChar(ch)) {
239
 
                        // Begin of here-doc (the line after the here-doc delimiter):
240
 
                        // Lexically, the here-doc starts from the next line after the >>, but the
241
 
                        // first line of here-doc seem to follow the style of the last EOL sequence
242
 
                        HereDoc.State = 2;
243
 
                        if (HereDoc.Quoted) {
244
 
                                if (state == SCE_SH_HERE_DELIM) {
245
 
                                        // Missing quote at end of string! We are stricter than bash.
246
 
                                        // Colour here-doc anyway while marking this bit as an error.
247
 
                                        state = SCE_SH_ERROR;
248
 
                                }
249
 
                                styler.ColourTo(i - 1, state);
250
 
                                // HereDoc.Quote always == '\''
251
 
                                state = SCE_SH_HERE_Q;
252
 
                        } else {
253
 
                                styler.ColourTo(i - 1, state);
254
 
                                // always switch
255
 
                                state = SCE_SH_HERE_Q;
256
 
                        }
257
 
                }
258
 
 
259
 
                if (state == SCE_SH_DEFAULT) {
260
 
                        if (ch == '\\') {       // escaped character
261
 
                                i++;
262
 
                                ch = chNext;
263
 
                                chNext = chNext2;
264
 
                                styler.ColourTo(i, SCE_SH_IDENTIFIER);
265
 
                        } else if (isdigit(ch)) {
266
 
                                state = SCE_SH_NUMBER;
267
 
                                numBase = BASH_BASE_DECIMAL;
268
 
                                if (ch == '0') {        // hex,octal
269
 
                                        if (chNext == 'x' || chNext == 'X') {
270
 
                                                numBase = BASH_BASE_HEX;
271
 
                                                i++;
272
 
                                                ch = chNext;
273
 
                                                chNext = chNext2;
274
 
                                        } else if (isdigit(chNext)) {
275
 
                                                numBase = BASH_BASE_OCTAL;
 
159
        if (initStyle == SCE_SH_STRING
 
160
         || initStyle == SCE_SH_BACKTICKS
 
161
         || initStyle == SCE_SH_CHARACTER
 
162
         || initStyle == SCE_SH_NUMBER
 
163
         || initStyle == SCE_SH_IDENTIFIER
 
164
         || initStyle == SCE_SH_COMMENTLINE) {
 
165
                while ((startPos > 1) && (styler.StyleAt(startPos - 1) == initStyle)) {
 
166
                        startPos--;
 
167
                }
 
168
                initStyle = SCE_SH_DEFAULT;
 
169
        }
 
170
 
 
171
        StyleContext sc(startPos, endPos - startPos, initStyle, styler);
 
172
 
 
173
        for (; sc.More(); sc.Forward()) {
 
174
 
 
175
                // Determine if the current state should terminate.
 
176
                switch (sc.state) {
 
177
                        case SCE_SH_OPERATOR:
 
178
                                sc.SetState(SCE_SH_DEFAULT);
 
179
                                break;
 
180
                        case SCE_SH_WORD:
 
181
                                // "." never used in Bash variable names but used in file names
 
182
                                if (!setWord.Contains(sc.ch)) {
 
183
                                        char s[1000];
 
184
                                        sc.GetCurrent(s, sizeof(s));
 
185
                                        if (s[0] != '-' &&      // for file operators
 
186
                                                !keywords.InList(s)) {
 
187
                                                sc.ChangeState(SCE_SH_IDENTIFIER);
276
188
                                        }
277
 
                                }
278
 
                        } else if (iswordstart(ch)) {
279
 
                                state = SCE_SH_WORD;
280
 
                                if (!iswordchar(chNext) && chNext != '+' && chNext != '-') {
281
 
                                        // We need that if length of word == 1!
282
 
                                        // This test is copied from the SCE_SH_WORD handler.
283
 
                                        classifyWordBash(styler.GetStartSegment(), i, keywords, styler);
284
 
                                        state = SCE_SH_DEFAULT;
285
 
                                }
286
 
                        } else if (ch == '#') {
287
 
                                state = SCE_SH_COMMENTLINE;
288
 
                        } else if (ch == '\"') {
289
 
                                state = SCE_SH_STRING;
290
 
                                Quote.New(1);
291
 
                                Quote.Open(ch);
292
 
                        } else if (ch == '\'') {
293
 
                                state = SCE_SH_CHARACTER;
294
 
                                Quote.New(1);
295
 
                                Quote.Open(ch);
296
 
                        } else if (ch == '`') {
297
 
                                state = SCE_SH_BACKTICKS;
298
 
                                Quote.New(1);
299
 
                                Quote.Open(ch);
300
 
                        } else if (ch == '$') {
301
 
                                if (chNext == '{') {
302
 
                                        state = SCE_SH_PARAM;
303
 
                                        goto startQuote;
304
 
                                } else if (chNext == '\'') {
305
 
                                        state = SCE_SH_CHARACTER;
306
 
                                        goto startQuote;
307
 
                                } else if (chNext == '"') {
308
 
                                        state = SCE_SH_STRING;
309
 
                                        goto startQuote;
310
 
                                } else if (chNext == '(' && chNext2 == '(') {
311
 
                                        styler.ColourTo(i, SCE_SH_OPERATOR);
312
 
                                        state = SCE_SH_DEFAULT;
313
 
                                        goto skipChar;
314
 
                                } else if (chNext == '(' || chNext == '`') {
315
 
                                        state = SCE_SH_BACKTICKS;
316
 
                                startQuote:
317
 
                                        Quote.New(1);
318
 
                                        Quote.Open(chNext);
319
 
                                        goto skipChar;
320
 
                                } else {
321
 
                                        state = SCE_SH_SCALAR;
322
 
                                skipChar:
323
 
                                        i++;
324
 
                                        ch = chNext;
325
 
                                        chNext = chNext2;
326
 
                                }
327
 
                        } else if (ch == '*') {
328
 
                                if (chNext == '*') {    // exponentiation
329
 
                                        i++;
330
 
                                        ch = chNext;
331
 
                                        chNext = chNext2;
332
 
                                }
333
 
                                styler.ColourTo(i, SCE_SH_OPERATOR);
334
 
                        } else if (ch == '<' && chNext == '<') {
335
 
                                state = SCE_SH_HERE_DELIM;
336
 
                                HereDoc.State = 0;
337
 
                                HereDoc.Indent = false;
338
 
                        } else if (ch == '-'    // file test operators
339
 
                                   && isSingleCharOp(chNext)
340
 
                                   && !isalnum((chNext2 = styler.SafeGetCharAt(i+2)))) {
341
 
                                styler.ColourTo(i + 1, SCE_SH_WORD);
342
 
                                state = SCE_SH_DEFAULT;
343
 
                                i++;
344
 
                                ch = chNext;
345
 
                                chNext = chNext2;
346
 
                        } else if (isBashOperator(ch)) {
347
 
                                styler.ColourTo(i, SCE_SH_OPERATOR);
348
 
                        } else {
349
 
                                // keep colouring defaults to make restart easier
350
 
                                styler.ColourTo(i, SCE_SH_DEFAULT);
351
 
                        }
352
 
                } else if (state == SCE_SH_NUMBER) {
353
 
                        int digit = translateBashDigit(ch);
354
 
                        if (numBase == BASH_BASE_DECIMAL) {
355
 
                                if (ch == '#') {
356
 
                                        numBase = getBashNumberBase(styler.GetStartSegment(), i - 1, styler);
357
 
                                        if (numBase == BASH_BASE_ERROR) // take the rest as comment
358
 
                                                goto numAtEnd;
359
 
                                } else if (!isdigit(ch))
360
 
                                        goto numAtEnd;
361
 
                        } else if (numBase == BASH_BASE_HEX) {
362
 
                                if ((digit < 16) || (digit >= 36 && digit <= 41)) {
363
 
                                        // hex digit 0-9a-fA-F
364
 
                                } else
365
 
                                        goto numAtEnd;
366
 
                        } else if (numBase == BASH_BASE_OCTAL ||
367
 
                                   numBase == BASH_BASE_OCTAL_ERROR) {
368
 
                                if (digit > 7) {
 
189
                                        sc.SetState(SCE_SH_DEFAULT);
 
190
                                }
 
191
                                break;
 
192
                        case SCE_SH_IDENTIFIER:
 
193
                                if (sc.chPrev == '\\') {        // for escaped chars
 
194
                                        sc.ForwardSetState(SCE_SH_DEFAULT);
 
195
                                } else if (!setWord.Contains(sc.ch)) {
 
196
                                        sc.SetState(SCE_SH_DEFAULT);
 
197
                                }
 
198
                                break;
 
199
                        case SCE_SH_NUMBER:
 
200
                                digit = translateBashDigit(sc.ch);
 
201
                                if (numBase == BASH_BASE_DECIMAL) {
 
202
                                        if (sc.ch == '#') {
 
203
                                                char s[10];
 
204
                                                sc.GetCurrent(s, sizeof(s));
 
205
                                                numBase = getBashNumberBase(s);
 
206
                                                if (numBase != BASH_BASE_ERROR)
 
207
                                                        break;
 
208
                                        } else if (IsADigit(sc.ch))
 
209
                                                break;
 
210
                                } else if (numBase == BASH_BASE_HEX) {
 
211
                                        if (IsADigit(sc.ch, 16))
 
212
                                                break;
 
213
#ifdef PEDANTIC_OCTAL
 
214
                                } else if (numBase == BASH_BASE_OCTAL ||
 
215
                                                   numBase == BASH_BASE_OCTAL_ERROR) {
 
216
                                        if (digit <= 7)
 
217
                                                break;
369
218
                                        if (digit <= 9) {
370
219
                                                numBase = BASH_BASE_OCTAL_ERROR;
371
 
                                        } else
372
 
                                                goto numAtEnd;
373
 
                                }
374
 
                        } else if (numBase == BASH_BASE_ERROR) {
375
 
                                if (digit > 9)
376
 
                                        goto numAtEnd;
377
 
                        } else {        // DD#DDDD number style handling
378
 
                                if (digit != BASH_BASE_ERROR) {
379
 
                                        if (numBase <= 36) {
380
 
                                                // case-insensitive if base<=36
381
 
                                                if (digit >= 36) digit -= 26;
 
220
                                                break;
382
221
                                        }
383
 
                                        if (digit >= numBase) {
 
222
#endif
 
223
                                } else if (numBase == BASH_BASE_ERROR) {
 
224
                                        if (digit <= 9)
 
225
                                                break;
 
226
                                } else {        // DD#DDDD number style handling
 
227
                                        if (digit != BASH_BASE_ERROR) {
 
228
                                                if (numBase <= 36) {
 
229
                                                        // case-insensitive if base<=36
 
230
                                                        if (digit >= 36) digit -= 26;
 
231
                                                }
 
232
                                                if (digit < numBase)
 
233
                                                        break;
384
234
                                                if (digit <= 9) {
385
235
                                                        numBase = BASH_BASE_ERROR;
386
 
                                                } else
387
 
                                                        goto numAtEnd;
 
236
                                                        break;
 
237
                                                }
388
238
                                        }
389
 
                                } else {
390
 
                        numAtEnd:
391
 
                                        if (numBase == BASH_BASE_ERROR ||
392
 
                                            numBase == BASH_BASE_OCTAL_ERROR)
393
 
                                                state = SCE_SH_ERROR;
394
 
                                        styler.ColourTo(i - 1, state);
395
 
                                        state = SCE_SH_DEFAULT;
396
 
                                        goto restartLexer;
397
 
                                }
398
 
                        }
399
 
                } else if (state == SCE_SH_WORD) {
400
 
                        if (!iswordchar(chNext) && chNext != '+' && chNext != '-') {
401
 
                                // "." never used in Bash variable names
402
 
                                // but used in file names
403
 
                                classifyWordBash(styler.GetStartSegment(), i, keywords, styler);
404
 
                                state = SCE_SH_DEFAULT;
405
 
                                ch = ' ';
406
 
                        }
407
 
                } else if (state == SCE_SH_IDENTIFIER) {
408
 
                        if (!iswordchar(chNext) && chNext != '+' && chNext != '-') {
409
 
                                styler.ColourTo(i, SCE_SH_IDENTIFIER);
410
 
                                state = SCE_SH_DEFAULT;
411
 
                                ch = ' ';
412
 
                        }
413
 
                } else {
414
 
                        if (state == SCE_SH_COMMENTLINE) {
415
 
                                if (ch == '\\' && isEOLChar(chNext)) {
 
239
                                }
 
240
                                // fallthrough when number is at an end or error
 
241
                                if (numBase == BASH_BASE_ERROR
 
242
#ifdef PEDANTIC_OCTAL
 
243
                                        || numBase == BASH_BASE_OCTAL_ERROR
 
244
#endif
 
245
                                ) {
 
246
                                        sc.ChangeState(SCE_SH_ERROR);
 
247
                                }
 
248
                                sc.SetState(SCE_SH_DEFAULT);
 
249
                                break;
 
250
                        case SCE_SH_COMMENTLINE:
 
251
                                if (sc.ch == '\\' && (sc.chNext == '\r' || sc.chNext == '\n')) {
416
252
                                        // comment continuation
417
 
                                        if (chNext == '\r' && chNext2 == '\n') {
418
 
                                                i += 2;
419
 
                                                ch = styler.SafeGetCharAt(i);
420
 
                                                chNext = styler.SafeGetCharAt(i + 1);
421
 
                                        } else {
422
 
                                                i++;
423
 
                                                ch = chNext;
424
 
                                                chNext = chNext2;
 
253
                                        sc.Forward();
 
254
                                        if (sc.ch == '\r' && sc.chNext == '\n') {
 
255
                                                sc.Forward();
425
256
                                        }
426
 
                                } else if (isEOLChar(ch)) {
427
 
                                        styler.ColourTo(i - 1, state);
428
 
                                        state = SCE_SH_DEFAULT;
429
 
                                        goto restartLexer;
430
 
                                } else if (isEOLChar(chNext)) {
431
 
                                        styler.ColourTo(i, state);
432
 
                                        state = SCE_SH_DEFAULT;
 
257
                                } else if (sc.atLineEnd) {
 
258
                                        sc.ForwardSetState(SCE_SH_DEFAULT);
433
259
                                }
434
 
                        } else if (state == SCE_SH_HERE_DELIM) {
435
 
                                //
 
260
                                break;
 
261
                        case SCE_SH_HERE_DELIM:
436
262
                                // From Bash info:
437
263
                                // ---------------
438
264
                                // Specifier format is: <<[-]WORD
440
266
                                // Whitespace acceptable after <<[-] operator
441
267
                                //
442
268
                                if (HereDoc.State == 0) { // '<<' encountered
443
 
                                        HereDoc.State = 1;
444
 
                                        HereDoc.Quote = chNext;
 
269
                                        HereDoc.Quote = sc.chNext;
445
270
                                        HereDoc.Quoted = false;
446
271
                                        HereDoc.DelimiterLength = 0;
447
272
                                        HereDoc.Delimiter[HereDoc.DelimiterLength] = '\0';
448
 
                                        if (chNext == '\'' || chNext == '\"') { // a quoted here-doc delimiter (' or ")
449
 
                                                i++;
450
 
                                                ch = chNext;
451
 
                                                chNext = chNext2;
 
273
                                        if (sc.chNext == '\'' || sc.chNext == '\"') {   // a quoted here-doc delimiter (' or ")
 
274
                                                sc.Forward();
452
275
                                                HereDoc.Quoted = true;
453
 
                                        } else if (!HereDoc.Indent && chNext == '-') {  // <<- indent case
 
276
                                                HereDoc.State = 1;
 
277
                                        } else if (!HereDoc.Indent && sc.chNext == '-') {       // <<- indent case
454
278
                                                HereDoc.Indent = true;
455
 
                                                HereDoc.State = 0;
456
 
                                        } else if (isalpha(chNext) || chNext == '_' || chNext == '\\'
457
 
                                                || chNext == '-' || chNext == '+' || chNext == '!') {
 
279
                                        } else if (setHereDoc.Contains(sc.chNext)) {
458
280
                                                // an unquoted here-doc delimiter, no special handling
459
 
                        // TODO check what exactly bash considers part of the delim
460
 
                                        } else if (chNext == '<') {     // HERE string <<<
461
 
                                                i++;
462
 
                                                ch = chNext;
463
 
                                                chNext = chNext2;
464
 
                                                styler.ColourTo(i, SCE_SH_HERE_DELIM);
465
 
                                                state = SCE_SH_DEFAULT;
466
 
                                                HereDoc.State = 0;
467
 
                                        } else if (isspacechar(chNext)) {
 
281
                                                // TODO check what exactly bash considers part of the delim
 
282
                                                HereDoc.State = 1;
 
283
                                        } else if (sc.chNext == '<') {  // HERE string <<<
 
284
                                                sc.Forward();
 
285
                                                sc.ForwardSetState(SCE_SH_DEFAULT);
 
286
                                        } else if (IsASpace(sc.chNext)) {
468
287
                                                // eat whitespace
469
 
                                                HereDoc.State = 0;
470
 
                                        } else if (isdigit(chNext) || chNext == '=' || chNext == '$') {
 
288
                                        } else if (setLeftShift.Contains(sc.chNext)) {
471
289
                                                // left shift << or <<= operator cases
472
 
                                                styler.ColourTo(i, SCE_SH_OPERATOR);
473
 
                                                state = SCE_SH_DEFAULT;
474
 
                                                HereDoc.State = 0;
 
290
                                                sc.ChangeState(SCE_SH_OPERATOR);
 
291
                                                sc.ForwardSetState(SCE_SH_DEFAULT);
475
292
                                        } else {
476
293
                                                // symbols terminates; deprecated zero-length delimiter
 
294
                                                HereDoc.State = 1;
477
295
                                        }
478
296
                                } else if (HereDoc.State == 1) { // collect the delimiter
479
297
                                        if (HereDoc.Quoted) { // a quoted here-doc delimiter
480
 
                                                if (ch == HereDoc.Quote) { // closing quote => end of delimiter
481
 
                                                        styler.ColourTo(i, state);
482
 
                                                        state = SCE_SH_DEFAULT;
 
298
                                                if (sc.ch == HereDoc.Quote) { // closing quote => end of delimiter
 
299
                                                        sc.ForwardSetState(SCE_SH_DEFAULT);
483
300
                                                } else {
484
 
                                                        if (ch == '\\' && chNext == HereDoc.Quote) { // escaped quote
485
 
                                                                i++;
486
 
                                                                ch = chNext;
487
 
                                                                chNext = chNext2;
 
301
                                                        if (sc.ch == '\\' && sc.chNext == HereDoc.Quote) { // escaped quote
 
302
                                                                sc.Forward();
488
303
                                                        }
489
 
                                                        HereDoc.Delimiter[HereDoc.DelimiterLength++] = ch;
490
 
                                                        HereDoc.Delimiter[HereDoc.DelimiterLength] = '\0';
 
304
                                                        HereDoc.Append(sc.ch);
491
305
                                                }
492
306
                                        } else { // an unquoted here-doc delimiter
493
 
                                                if (isalnum(ch) || ch == '_' || ch == '-' || ch == '+' || ch == '!') {
494
 
                                                        HereDoc.Delimiter[HereDoc.DelimiterLength++] = ch;
495
 
                                                        HereDoc.Delimiter[HereDoc.DelimiterLength] = '\0';
496
 
                                                } else if (ch == '\\') {
 
307
                                                if (setHereDoc2.Contains(sc.ch)) {
 
308
                                                        HereDoc.Append(sc.ch);
 
309
                                                } else if (sc.ch == '\\') {
497
310
                                                        // skip escape prefix
498
311
                                                } else {
499
 
                                                        styler.ColourTo(i - 1, state);
500
 
                                                        state = SCE_SH_DEFAULT;
501
 
                                                        goto restartLexer;
502
 
                                                }
503
 
                                        }
504
 
                                        if (HereDoc.DelimiterLength >= HERE_DELIM_MAX - 1) {
505
 
                                                styler.ColourTo(i - 1, state);
506
 
                                                state = SCE_SH_ERROR;
507
 
                                                goto restartLexer;
508
 
                                        }
509
 
                                }
510
 
                        } else if (HereDoc.State == 2) {
511
 
                                // state == SCE_SH_HERE_Q
512
 
                                if (isMatch(styler, lengthDoc, i, HereDoc.Delimiter)) {
513
 
                                        if (!HereDoc.Indent && isEOLChar(chPrev)) {
514
 
                                        endHereDoc:
515
 
                                                // standard HERE delimiter
516
 
                                                i += HereDoc.DelimiterLength;
517
 
                                                chPrev = styler.SafeGetCharAt(i - 1);
518
 
                                                ch = styler.SafeGetCharAt(i);
519
 
                                                if (isEOLChar(ch)) {
520
 
                                                        styler.ColourTo(i - 1, state);
521
 
                                                        state = SCE_SH_DEFAULT;
522
 
                                                        HereDoc.State = 0;
523
 
                                                        goto restartLexer;
524
 
                                                }
525
 
                                                chNext = styler.SafeGetCharAt(i + 1);
526
 
                                        } else if (HereDoc.Indent) {
527
 
                                                // indented HERE delimiter
528
 
                                                unsigned int bk = (i > 0)? i - 1: 0;
529
 
                                                while (i > 0) {
530
 
                                                        ch = styler.SafeGetCharAt(bk--);
531
 
                                                        if (isEOLChar(ch)) {
532
 
                                                                goto endHereDoc;
533
 
                                                        } else if (!isspacechar(ch)) {
534
 
                                                                break;  // got leading non-whitespace
535
 
                                                        }
536
 
                                                }
537
 
                                        }
538
 
                                }
539
 
                        } else if (state == SCE_SH_SCALAR) {    // variable names
540
 
                                if (isEndVar(ch)) {
541
 
                                        if ((state == SCE_SH_SCALAR)
542
 
                                            && i == (styler.GetStartSegment() + 1)) {
 
312
                                                        sc.SetState(SCE_SH_DEFAULT);
 
313
                                                }
 
314
                                        }
 
315
                                        if (HereDoc.DelimiterLength >= HERE_DELIM_MAX - 1) {    // force blowup
 
316
                                                sc.SetState(SCE_SH_ERROR);
 
317
                                                HereDoc.State = 0;
 
318
                                        }
 
319
                                }
 
320
                                break;
 
321
                        case SCE_SH_HERE_Q:
 
322
                                // HereDoc.State == 2
 
323
                                if (sc.atLineStart) {
 
324
                                        sc.SetState(SCE_SH_HERE_Q);
 
325
                                        int prefixws = 0;
 
326
                                        while (IsASpace(sc.ch) && !sc.atLineEnd) {      // whitespace prefix
 
327
                                                sc.Forward();
 
328
                                                prefixws++;
 
329
                                        }
 
330
                                        if (prefixws > 0)
 
331
                                                sc.SetState(SCE_SH_HERE_Q);
 
332
                                        while (!sc.atLineEnd) {
 
333
                                                sc.Forward();
 
334
                                        }
 
335
                                        char s[HERE_DELIM_MAX];
 
336
                                        sc.GetCurrent(s, sizeof(s));
 
337
                                        if (sc.LengthCurrent() == 0)
 
338
                                                break;
 
339
                                        if (s[strlen(s) - 1] == '\r')
 
340
                                                s[strlen(s) - 1] = '\0';
 
341
                                        if (strcmp(HereDoc.Delimiter, s) == 0) {
 
342
                                                if ((prefixws > 0 && HereDoc.Indent) || // indentation rule
 
343
                                                        (prefixws == 0 && !HereDoc.Indent)) {
 
344
                                                        sc.SetState(SCE_SH_DEFAULT);
 
345
                                                        break;
 
346
                                                }
 
347
                                        }
 
348
                                }
 
349
                                break;
 
350
                        case SCE_SH_SCALAR:     // variable names
 
351
                                if (!setParam.Contains(sc.ch)) {
 
352
                                        if (sc.LengthCurrent() == 1) {
543
353
                                                // Special variable: $(, $_ etc.
544
 
                                                styler.ColourTo(i, state);
545
 
                                                state = SCE_SH_DEFAULT;
 
354
                                                sc.ForwardSetState(SCE_SH_DEFAULT);
546
355
                                        } else {
547
 
                                                styler.ColourTo(i - 1, state);
548
 
                                                state = SCE_SH_DEFAULT;
549
 
                                                goto restartLexer;
 
356
                                                sc.SetState(SCE_SH_DEFAULT);
550
357
                                        }
551
358
                                }
552
 
                        } else if (state == SCE_SH_STRING
553
 
                                || state == SCE_SH_CHARACTER
554
 
                                || state == SCE_SH_BACKTICKS
555
 
                                || state == SCE_SH_PARAM
556
 
                                ) {
557
 
                                if (!Quote.Down && !isspacechar(ch)) {
558
 
                                        Quote.Open(ch);
559
 
                                } else if (ch == '\\' && Quote.Up != '\\') {
560
 
                                        i++;
561
 
                                        ch = chNext;
562
 
                                        chNext = styler.SafeGetCharAt(i + 1);
563
 
                                } else if (ch == Quote.Down) {
 
359
                                break;
 
360
                        case SCE_SH_STRING:     // delimited styles
 
361
                        case SCE_SH_CHARACTER:
 
362
                        case SCE_SH_BACKTICKS:
 
363
                        case SCE_SH_PARAM:
 
364
                                if (sc.ch == '\\' && Quote.Up != '\\') {
 
365
                                        sc.Forward();
 
366
                                } else if (sc.ch == Quote.Down) {
564
367
                                        Quote.Count--;
565
368
                                        if (Quote.Count == 0) {
566
 
                                                Quote.Rep--;
567
 
                                                if (Quote.Rep <= 0) {
568
 
                                                        styler.ColourTo(i, state);
569
 
                                                        state = SCE_SH_DEFAULT;
570
 
                                                        ch = ' ';
571
 
                                                }
572
 
                                                if (Quote.Up == Quote.Down) {
573
 
                                                        Quote.Count++;
574
 
                                                }
 
369
                                                sc.ForwardSetState(SCE_SH_DEFAULT);
575
370
                                        }
576
 
                                } else if (ch == Quote.Up) {
 
371
                                } else if (sc.ch == Quote.Up) {
577
372
                                        Quote.Count++;
578
373
                                }
579
 
                        }
580
 
                }
581
 
                if (state == SCE_SH_ERROR) {
582
 
                        break;
583
 
                }
584
 
                chPrev = ch;
 
374
                                break;
 
375
                }
 
376
 
 
377
                // Must check end of HereDoc state 1 before default state is handled
 
378
                if (HereDoc.State == 1 && sc.atLineEnd) {
 
379
                        // Begin of here-doc (the line after the here-doc delimiter):
 
380
                        // Lexically, the here-doc starts from the next line after the >>, but the
 
381
                        // first line of here-doc seem to follow the style of the last EOL sequence
 
382
                        HereDoc.State = 2;
 
383
                        if (HereDoc.Quoted) {
 
384
                                if (sc.state == SCE_SH_HERE_DELIM) {
 
385
                                        // Missing quote at end of string! We are stricter than bash.
 
386
                                        // Colour here-doc anyway while marking this bit as an error.
 
387
                                        sc.ChangeState(SCE_SH_ERROR);
 
388
                                }
 
389
                                // HereDoc.Quote always == '\''
 
390
                        }
 
391
                        sc.SetState(SCE_SH_HERE_Q);
 
392
                }
 
393
 
 
394
                // Determine if a new state should be entered.
 
395
                if (sc.state == SCE_SH_DEFAULT) {
 
396
                        if (sc.ch == '\\') {    // escaped character
 
397
                                sc.SetState(SCE_SH_IDENTIFIER);
 
398
                        } else if (IsADigit(sc.ch)) {
 
399
                                sc.SetState(SCE_SH_NUMBER);
 
400
                                numBase = BASH_BASE_DECIMAL;
 
401
                                if (sc.ch == '0') {     // hex,octal
 
402
                                        if (sc.chNext == 'x' || sc.chNext == 'X') {
 
403
                                                numBase = BASH_BASE_HEX;
 
404
                                                sc.Forward();
 
405
                                        } else if (IsADigit(sc.chNext)) {
 
406
#ifdef PEDANTIC_OCTAL
 
407
                                                numBase = BASH_BASE_OCTAL;
 
408
#else
 
409
                                                numBase = BASH_BASE_HEX;
 
410
#endif
 
411
                                        }
 
412
                                }
 
413
                        } else if (setWordStart.Contains(sc.ch)) {
 
414
                                sc.SetState(SCE_SH_WORD);
 
415
                        } else if (sc.ch == '#') {
 
416
                                sc.SetState(SCE_SH_COMMENTLINE);
 
417
                        } else if (sc.ch == '\"') {
 
418
                                sc.SetState(SCE_SH_STRING);
 
419
                                Quote.Start(sc.ch);
 
420
                        } else if (sc.ch == '\'') {
 
421
                                sc.SetState(SCE_SH_CHARACTER);
 
422
                                Quote.Start(sc.ch);
 
423
                        } else if (sc.ch == '`') {
 
424
                                sc.SetState(SCE_SH_BACKTICKS);
 
425
                                Quote.Start(sc.ch);
 
426
                        } else if (sc.ch == '$') {
 
427
                                sc.SetState(SCE_SH_SCALAR);
 
428
                                sc.Forward();
 
429
                                if (sc.ch == '{') {
 
430
                                        sc.ChangeState(SCE_SH_PARAM);
 
431
                                } else if (sc.ch == '\'') {
 
432
                                        sc.ChangeState(SCE_SH_CHARACTER);
 
433
                                } else if (sc.ch == '"') {
 
434
                                        sc.ChangeState(SCE_SH_STRING);
 
435
                                } else if (sc.ch == '(' || sc.ch == '`') {
 
436
                                        sc.ChangeState(SCE_SH_BACKTICKS);
 
437
                                        if (sc.chNext == '(') { // $(( is lexed as operator
 
438
                                                sc.ChangeState(SCE_SH_OPERATOR);
 
439
                                        }
 
440
                                } else {
 
441
                                        continue;       // scalar has no delimiter pair
 
442
                                }
 
443
                                // fallthrough, open delim for $[{'"(`]
 
444
                                Quote.Start(sc.ch);
 
445
                        } else if (sc.Match('<', '<')) {
 
446
                                sc.SetState(SCE_SH_HERE_DELIM);
 
447
                                HereDoc.State = 0;
 
448
                                HereDoc.Indent = false;
 
449
                        } else if (sc.ch == '-' &&      // one-char file test operators
 
450
                                           setSingleCharOp.Contains(sc.chNext) &&
 
451
                                           !setWord.Contains(sc.GetRelative(2)) &&
 
452
                                           IsASpace(sc.chPrev)) {
 
453
                                sc.SetState(SCE_SH_WORD);
 
454
                                sc.Forward();
 
455
                        } else if (setBashOperator.Contains(sc.ch)) {
 
456
                                sc.SetState(SCE_SH_OPERATOR);
 
457
                        }
 
458
                }
585
459
        }
586
 
        styler.ColourTo(lengthDoc - 1, state);
 
460
        sc.Complete();
587
461
}
588
462
 
589
463
static bool IsCommentLine(int line, Accessor &styler) {
600
474
}
601
475
 
602
476
static void FoldBashDoc(unsigned int startPos, int length, int, WordList *[],
603
 
                            Accessor &styler) {
 
477
                                                Accessor &styler) {
604
478
        bool foldComment = styler.GetPropertyInt("fold.comment") != 0;
605
479
        bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
606
480
        unsigned int endPos = startPos + length;
616
490
                int style = styleNext;
617
491
                styleNext = styler.StyleAt(i + 1);
618
492
                bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
619
 
        // Comment folding
 
493
                // Comment folding
620
494
                if (foldComment && atEOL && IsCommentLine(lineCurrent, styler))
621
 
        {
622
 
            if (!IsCommentLine(lineCurrent - 1, styler)
623
 
                && IsCommentLine(lineCurrent + 1, styler))
624
 
                levelCurrent++;
625
 
            else if (IsCommentLine(lineCurrent - 1, styler)
626
 
                     && !IsCommentLine(lineCurrent+1, styler))
627
 
                levelCurrent--;
628
 
        }
629
 
                if (style == SCE_C_OPERATOR) {
 
495
                {
 
496
                        if (!IsCommentLine(lineCurrent - 1, styler)
 
497
                                && IsCommentLine(lineCurrent + 1, styler))
 
498
                                levelCurrent++;
 
499
                        else if (IsCommentLine(lineCurrent - 1, styler)
 
500
                                         && !IsCommentLine(lineCurrent + 1, styler))
 
501
                                levelCurrent--;
 
502
                }
 
503
                if (style == SCE_SH_OPERATOR) {
630
504
                        if (ch == '{') {
631
505
                                levelCurrent++;
632
506
                        } else if (ch == '}') {