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