1 // Scintilla source code edit control
2 /** @file LexCrontab.cxx
3 ** Lexer to use with extended crontab files used by a powerful
4 ** Windows scheduler/event monitor/automation manager nnCron.
5 ** (http://nemtsev.eserv.ru/)
6 **/
7 // Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
8 // The License.txt file describes the conditions under which this software may be distributed.
9
10 #include <stdlib.h>
11 #include <string.h>
12 #include <stdio.h>
13 #include <stdarg.h>
14 #include <assert.h>
15 #include <ctype.h>
16
17 #include "ILexer.h"
18 #include "Scintilla.h"
19 #include "SciLexer.h"
20
21 #include "WordList.h"
22 #include "LexAccessor.h"
23 #include "Accessor.h"
24 #include "StyleContext.h"
25 #include "CharacterSet.h"
26 #include "LexerModule.h"
27
28 #ifdef SCI_NAMESPACE
29 using namespace Scintilla;
30 #endif
31
ColouriseNncrontabDoc(unsigned int startPos,int length,int,WordList * keywordLists[],Accessor & styler)32 static void ColouriseNncrontabDoc(unsigned int startPos, int length, int, WordList
33 *keywordLists[], Accessor &styler)
34 {
35 int state = SCE_NNCRONTAB_DEFAULT;
36 char chNext = styler[startPos];
37 int lengthDoc = startPos + length;
38 // create a buffer large enough to take the largest chunk...
39 char *buffer = new char[length];
40 int bufferCount = 0;
41 // used when highliting environment variables inside quoted string:
42 bool insideString = false;
43
44 // this assumes that we have 3 keyword list in conf.properties
45 WordList §ion = *keywordLists[0];
46 WordList &keyword = *keywordLists[1];
47 WordList &modifier = *keywordLists[2];
48
49 // go through all provided text segment
50 // using the hand-written state machine shown below
51 styler.StartAt(startPos);
52 styler.StartSegment(startPos);
53 for (int i = startPos; i < lengthDoc; i++) {
54 char ch = chNext;
55 chNext = styler.SafeGetCharAt(i + 1);
56
57 if (styler.IsLeadByte(ch)) {
58 chNext = styler.SafeGetCharAt(i + 2);
59 i++;
60 continue;
61 }
62 switch(state) {
63 case SCE_NNCRONTAB_DEFAULT:
64 if( ch == '\n' || ch == '\r' || ch == '\t' || ch == ' ') {
65 // whitespace is simply ignored here...
66 styler.ColourTo(i,SCE_NNCRONTAB_DEFAULT);
67 break;
68 } else if( ch == '#' && styler.SafeGetCharAt(i+1) == '(') {
69 // signals the start of a task...
70 state = SCE_NNCRONTAB_TASK;
71 styler.ColourTo(i,SCE_NNCRONTAB_TASK);
72 }
73 else if( ch == '\\' && (styler.SafeGetCharAt(i+1) == ' ' ||
74 styler.SafeGetCharAt(i+1) == '\t')) {
75 // signals the start of an extended comment...
76 state = SCE_NNCRONTAB_COMMENT;
77 styler.ColourTo(i,SCE_NNCRONTAB_COMMENT);
78 } else if( ch == '#' ) {
79 // signals the start of a plain comment...
80 state = SCE_NNCRONTAB_COMMENT;
81 styler.ColourTo(i,SCE_NNCRONTAB_COMMENT);
82 } else if( ch == ')' && styler.SafeGetCharAt(i+1) == '#') {
83 // signals the end of a task...
84 state = SCE_NNCRONTAB_TASK;
85 styler.ColourTo(i,SCE_NNCRONTAB_TASK);
86 } else if( ch == '"') {
87 state = SCE_NNCRONTAB_STRING;
88 styler.ColourTo(i,SCE_NNCRONTAB_STRING);
89 } else if( ch == '%') {
90 // signals environment variables
91 state = SCE_NNCRONTAB_ENVIRONMENT;
92 styler.ColourTo(i,SCE_NNCRONTAB_ENVIRONMENT);
93 } else if( ch == '<' && styler.SafeGetCharAt(i+1) == '%') {
94 // signals environment variables
95 state = SCE_NNCRONTAB_ENVIRONMENT;
96 styler.ColourTo(i,SCE_NNCRONTAB_ENVIRONMENT);
97 } else if( ch == '*' ) {
98 // signals an asterisk
99 // no state jump necessary for this simple case...
100 styler.ColourTo(i,SCE_NNCRONTAB_ASTERISK);
101 } else if( (isascii(ch) && isalpha(ch)) || ch == '<' ) {
102 // signals the start of an identifier
103 bufferCount = 0;
104 buffer[bufferCount++] = ch;
105 state = SCE_NNCRONTAB_IDENTIFIER;
106 } else if( isascii(ch) && isdigit(ch) ) {
107 // signals the start of a number
108 bufferCount = 0;
109 buffer[bufferCount++] = ch;
110 state = SCE_NNCRONTAB_NUMBER;
111 } else {
112 // style it the default style..
113 styler.ColourTo(i,SCE_NNCRONTAB_DEFAULT);
114 }
115 break;
116
117 case SCE_NNCRONTAB_COMMENT:
118 // if we find a newline here,
119 // we simply go to default state
120 // else continue to work on it...
121 if( ch == '\n' || ch == '\r' ) {
122 state = SCE_NNCRONTAB_DEFAULT;
123 } else {
124 styler.ColourTo(i,SCE_NNCRONTAB_COMMENT);
125 }
126 break;
127
128 case SCE_NNCRONTAB_TASK:
129 // if we find a newline here,
130 // we simply go to default state
131 // else continue to work on it...
132 if( ch == '\n' || ch == '\r' ) {
133 state = SCE_NNCRONTAB_DEFAULT;
134 } else {
135 styler.ColourTo(i,SCE_NNCRONTAB_TASK);
136 }
137 break;
138
139 case SCE_NNCRONTAB_STRING:
140 if( ch == '%' ) {
141 state = SCE_NNCRONTAB_ENVIRONMENT;
142 insideString = true;
143 styler.ColourTo(i-1,SCE_NNCRONTAB_STRING);
144 break;
145 }
146 // if we find the end of a string char, we simply go to default state
147 // else we're still dealing with an string...
148 if( (ch == '"' && styler.SafeGetCharAt(i-1)!='\\') ||
149 (ch == '\n') || (ch == '\r') ) {
150 state = SCE_NNCRONTAB_DEFAULT;
151 }
152 styler.ColourTo(i,SCE_NNCRONTAB_STRING);
153 break;
154
155 case SCE_NNCRONTAB_ENVIRONMENT:
156 // if we find the end of a string char, we simply go to default state
157 // else we're still dealing with an string...
158 if( ch == '%' && insideString ) {
159 state = SCE_NNCRONTAB_STRING;
160 insideString = false;
161 break;
162 }
163 if( (ch == '%' && styler.SafeGetCharAt(i-1)!='\\')
164 || (ch == '\n') || (ch == '\r') || (ch == '>') ) {
165 state = SCE_NNCRONTAB_DEFAULT;
166 styler.ColourTo(i,SCE_NNCRONTAB_ENVIRONMENT);
167 break;
168 }
169 styler.ColourTo(i+1,SCE_NNCRONTAB_ENVIRONMENT);
170 break;
171
172 case SCE_NNCRONTAB_IDENTIFIER:
173 // stay in CONF_IDENTIFIER state until we find a non-alphanumeric
174 if( (isascii(ch) && isalnum(ch)) || (ch == '_') || (ch == '-') || (ch == '/') ||
175 (ch == '$') || (ch == '.') || (ch == '<') || (ch == '>') ||
176 (ch == '@') ) {
177 buffer[bufferCount++] = ch;
178 } else {
179 state = SCE_NNCRONTAB_DEFAULT;
180 buffer[bufferCount] = '\0';
181
182 // check if the buffer contains a keyword,
183 // and highlight it if it is a keyword...
184 if(section.InList(buffer)) {
185 styler.ColourTo(i,SCE_NNCRONTAB_SECTION );
186 } else if(keyword.InList(buffer)) {
187 styler.ColourTo(i-1,SCE_NNCRONTAB_KEYWORD );
188 } // else if(strchr(buffer,'/') || strchr(buffer,'.')) {
189 // styler.ColourTo(i-1,SCE_NNCRONTAB_EXTENSION);
190 // }
191 else if(modifier.InList(buffer)) {
192 styler.ColourTo(i-1,SCE_NNCRONTAB_MODIFIER );
193 } else {
194 styler.ColourTo(i-1,SCE_NNCRONTAB_DEFAULT);
195 }
196 // push back the faulty character
197 chNext = styler[i--];
198 }
199 break;
200
201 case SCE_NNCRONTAB_NUMBER:
202 // stay in CONF_NUMBER state until we find a non-numeric
203 if( isascii(ch) && isdigit(ch) /* || ch == '.' */ ) {
204 buffer[bufferCount++] = ch;
205 } else {
206 state = SCE_NNCRONTAB_DEFAULT;
207 buffer[bufferCount] = '\0';
208 // Colourize here... (normal number)
209 styler.ColourTo(i-1,SCE_NNCRONTAB_NUMBER);
210 // push back a character
211 chNext = styler[i--];
212 }
213 break;
214 }
215 }
216 delete []buffer;
217 }
218
219 static const char * const cronWordListDesc[] = {
220 "Section keywords and Forth words",
221 "nnCrontab keywords",
222 "Modifiers",
223 0
224 };
225
226 LexerModule lmNncrontab(SCLEX_NNCRONTAB, ColouriseNncrontabDoc, "nncrontab", 0, cronWordListDesc);
227