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