1 // Scintilla source code edit control
2 /** @file LexHex.cxx
3  ** Lexers for Motorola S-Record, Intel HEX and Tektronix extended HEX.
4  **
5  ** Written by Markus Heidelberg
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 /*
11  *  Motorola S-Record
12  * ===============================
13  *
14  * Each record (line) is built as follows:
15  *
16  *    field       digits          states
17  *
18  *  +----------+
19  *  | start    |  1 ('S')         SCE_HEX_RECSTART
20  *  +----------+
21  *  | type     |  1               SCE_HEX_RECTYPE, (SCE_HEX_RECTYPE_UNKNOWN)
22  *  +----------+
23  *  | count    |  2               SCE_HEX_BYTECOUNT, SCE_HEX_BYTECOUNT_WRONG
24  *  +----------+
25  *  | address  |  4/6/8           SCE_HEX_NOADDRESS, SCE_HEX_DATAADDRESS, SCE_HEX_RECCOUNT, SCE_HEX_STARTADDRESS, (SCE_HEX_ADDRESSFIELD_UNKNOWN)
26  *  +----------+
27  *  | data     |  0..504/502/500  SCE_HEX_DATA_ODD, SCE_HEX_DATA_EVEN, SCE_HEX_DATA_EMPTY, (SCE_HEX_DATA_UNKNOWN)
28  *  +----------+
29  *  | checksum |  2               SCE_HEX_CHECKSUM, SCE_HEX_CHECKSUM_WRONG
30  *  +----------+
31  *
32  *
33  *  Intel HEX
34  * ===============================
35  *
36  * Each record (line) is built as follows:
37  *
38  *    field       digits          states
39  *
40  *  +----------+
41  *  | start    |  1 (':')         SCE_HEX_RECSTART
42  *  +----------+
43  *  | count    |  2               SCE_HEX_BYTECOUNT, SCE_HEX_BYTECOUNT_WRONG
44  *  +----------+
45  *  | address  |  4               SCE_HEX_NOADDRESS, SCE_HEX_DATAADDRESS, (SCE_HEX_ADDRESSFIELD_UNKNOWN)
46  *  +----------+
47  *  | type     |  2               SCE_HEX_RECTYPE, (SCE_HEX_RECTYPE_UNKNOWN)
48  *  +----------+
49  *  | data     |  0..510          SCE_HEX_DATA_ODD, SCE_HEX_DATA_EVEN, SCE_HEX_DATA_EMPTY, SCE_HEX_EXTENDEDADDRESS, SCE_HEX_STARTADDRESS, (SCE_HEX_DATA_UNKNOWN)
50  *  +----------+
51  *  | checksum |  2               SCE_HEX_CHECKSUM, SCE_HEX_CHECKSUM_WRONG
52  *  +----------+
53  *
54  *
55  * Folding:
56  *
57  *   Data records (type 0x00), which follow an extended address record (type
58  *   0x02 or 0x04), can be folded. The extended address record is the fold
59  *   point at fold level 0, the corresponding data records are set to level 1.
60  *
61  *   Any record, which is not a data record, sets the fold level back to 0.
62  *   Any line, which is not a record (blank lines and lines starting with a
63  *   character other than ':'), leaves the fold level unchanged.
64  *
65  *
66  *  Tektronix extended HEX
67  * ===============================
68  *
69  * Each record (line) is built as follows:
70  *
71  *    field       digits          states
72  *
73  *  +----------+
74  *  | start    |  1 ('%')         SCE_HEX_RECSTART
75  *  +----------+
76  *  | length   |  2               SCE_HEX_BYTECOUNT, SCE_HEX_BYTECOUNT_WRONG
77  *  +----------+
78  *  | type     |  1               SCE_HEX_RECTYPE, (SCE_HEX_RECTYPE_UNKNOWN)
79  *  +----------+
80  *  | checksum |  2               SCE_HEX_CHECKSUM, SCE_HEX_CHECKSUM_WRONG
81  *  +----------+
82  *  | address  |  9               SCE_HEX_DATAADDRESS, SCE_HEX_STARTADDRESS, (SCE_HEX_ADDRESSFIELD_UNKNOWN)
83  *  +----------+
84  *  | data     |  0..241          SCE_HEX_DATA_ODD, SCE_HEX_DATA_EVEN
85  *  +----------+
86  *
87  *
88  *  General notes for all lexers
89  * ===============================
90  *
91  * - Depending on where the helper functions are invoked, some of them have to
92  *   read beyond the current position. In case of malformed data (record too
93  *   short), it has to be ensured that this either does not have bad influence
94  *   or will be captured deliberately.
95  *
96  * - States in parentheses in the upper format descriptions indicate that they
97  *   should not appear in a valid hex file.
98  *
99  * - State SCE_HEX_GARBAGE means garbage data after the intended end of the
100  *   record, the line is too long then. This state is used in all lexers.
101  */
102 
103 #include <stdlib.h>
104 #include <string.h>
105 #include <stdio.h>
106 #include <stdarg.h>
107 #include <assert.h>
108 #include <ctype.h>
109 
110 #include "ILexer.h"
111 #include "Scintilla.h"
112 #include "SciLexer.h"
113 
114 #include "WordList.h"
115 #include "LexAccessor.h"
116 #include "Accessor.h"
117 #include "StyleContext.h"
118 #include "CharacterSet.h"
119 #include "LexerModule.h"
120 
121 using namespace Scintilla;
122 
123 // prototypes for general helper functions
124 static inline bool IsNewline(const int ch);
125 static int GetHexaNibble(char hd);
126 static int GetHexaChar(char hd1, char hd2);
127 static int GetHexaChar(Sci_PositionU pos, Accessor &styler);
128 static bool ForwardWithinLine(StyleContext &sc, Sci_Position nb = 1);
129 static bool PosInSameRecord(Sci_PositionU pos1, Sci_PositionU pos2, Accessor &styler);
130 static Sci_Position CountByteCount(Sci_PositionU startPos, Sci_Position uncountedDigits, Accessor &styler);
131 static int CalcChecksum(Sci_PositionU startPos, Sci_Position cnt, bool twosCompl, Accessor &styler);
132 
133 // prototypes for file format specific helper functions
134 static Sci_PositionU GetSrecRecStartPosition(Sci_PositionU pos, Accessor &styler);
135 static int GetSrecByteCount(Sci_PositionU recStartPos, Accessor &styler);
136 static Sci_Position CountSrecByteCount(Sci_PositionU recStartPos, Accessor &styler);
137 static int GetSrecAddressFieldSize(Sci_PositionU recStartPos, Accessor &styler);
138 static int GetSrecAddressFieldType(Sci_PositionU recStartPos, Accessor &styler);
139 static int GetSrecDataFieldType(Sci_PositionU recStartPos, Accessor &styler);
140 static Sci_Position GetSrecRequiredDataFieldSize(Sci_PositionU recStartPos, Accessor &styler);
141 static int GetSrecChecksum(Sci_PositionU recStartPos, Accessor &styler);
142 static int CalcSrecChecksum(Sci_PositionU recStartPos, Accessor &styler);
143 
144 static Sci_PositionU GetIHexRecStartPosition(Sci_PositionU pos, Accessor &styler);
145 static int GetIHexByteCount(Sci_PositionU recStartPos, Accessor &styler);
146 static Sci_Position CountIHexByteCount(Sci_PositionU recStartPos, Accessor &styler);
147 static int GetIHexAddressFieldType(Sci_PositionU recStartPos, Accessor &styler);
148 static int GetIHexDataFieldType(Sci_PositionU recStartPos, Accessor &styler);
149 static int GetIHexRequiredDataFieldSize(Sci_PositionU recStartPos, Accessor &styler);
150 static int GetIHexChecksum(Sci_PositionU recStartPos, Accessor &styler);
151 static int CalcIHexChecksum(Sci_PositionU recStartPos, Accessor &styler);
152 
153 static int GetTEHexDigitCount(Sci_PositionU recStartPos, Accessor &styler);
154 static Sci_Position CountTEHexDigitCount(Sci_PositionU recStartPos, Accessor &styler);
155 static int GetTEHexAddressFieldType(Sci_PositionU recStartPos, Accessor &styler);
156 static int GetTEHexChecksum(Sci_PositionU recStartPos, Accessor &styler);
157 static int CalcTEHexChecksum(Sci_PositionU recStartPos, Accessor &styler);
158 
IsNewline(const int ch)159 static inline bool IsNewline(const int ch)
160 {
161     return (ch == '\n' || ch == '\r');
162 }
163 
GetHexaNibble(char hd)164 static int GetHexaNibble(char hd)
165 {
166 	int hexValue = 0;
167 
168 	if (hd >= '0' && hd <= '9') {
169 		hexValue += hd - '0';
170 	} else if (hd >= 'A' && hd <= 'F') {
171 		hexValue += hd - 'A' + 10;
172 	} else if (hd >= 'a' && hd <= 'f') {
173 		hexValue += hd - 'a' + 10;
174 	} else {
175 		return -1;
176 	}
177 
178 	return hexValue;
179 }
180 
GetHexaChar(char hd1,char hd2)181 static int GetHexaChar(char hd1, char hd2)
182 {
183 	int hexValue = 0;
184 
185 	if (hd1 >= '0' && hd1 <= '9') {
186 		hexValue += 16 * (hd1 - '0');
187 	} else if (hd1 >= 'A' && hd1 <= 'F') {
188 		hexValue += 16 * (hd1 - 'A' + 10);
189 	} else if (hd1 >= 'a' && hd1 <= 'f') {
190 		hexValue += 16 * (hd1 - 'a' + 10);
191 	} else {
192 		return -1;
193 	}
194 
195 	if (hd2 >= '0' && hd2 <= '9') {
196 		hexValue += hd2 - '0';
197 	} else if (hd2 >= 'A' && hd2 <= 'F') {
198 		hexValue += hd2 - 'A' + 10;
199 	} else if (hd2 >= 'a' && hd2 <= 'f') {
200 		hexValue += hd2 - 'a' + 10;
201 	} else {
202 		return -1;
203 	}
204 
205 	return hexValue;
206 }
207 
GetHexaChar(Sci_PositionU pos,Accessor & styler)208 static int GetHexaChar(Sci_PositionU pos, Accessor &styler)
209 {
210 	char highNibble, lowNibble;
211 
212 	highNibble = styler.SafeGetCharAt(pos);
213 	lowNibble = styler.SafeGetCharAt(pos + 1);
214 
215 	return GetHexaChar(highNibble, lowNibble);
216 }
217 
218 // Forward <nb> characters, but abort (and return false) if hitting the line
219 // end. Return true if forwarding within the line was possible.
220 // Avoids influence on highlighting of the subsequent line if the current line
221 // is malformed (too short).
ForwardWithinLine(StyleContext & sc,Sci_Position nb)222 static bool ForwardWithinLine(StyleContext &sc, Sci_Position nb)
223 {
224 	for (Sci_Position i = 0; i < nb; i++) {
225 		if (sc.atLineEnd) {
226 			// line is too short
227 			sc.SetState(SCE_HEX_DEFAULT);
228 			sc.Forward();
229 			return false;
230 		} else {
231 			sc.Forward();
232 		}
233 	}
234 
235 	return true;
236 }
237 
238 // Checks whether the given positions are in the same record.
PosInSameRecord(Sci_PositionU pos1,Sci_PositionU pos2,Accessor & styler)239 static bool PosInSameRecord(Sci_PositionU pos1, Sci_PositionU pos2, Accessor &styler)
240 {
241 	return styler.GetLine(pos1) == styler.GetLine(pos2);
242 }
243 
244 // Count the number of digit pairs from <startPos> till end of record, ignoring
245 // <uncountedDigits> digits.
246 // If the record is too short, a negative count may be returned.
CountByteCount(Sci_PositionU startPos,Sci_Position uncountedDigits,Accessor & styler)247 static Sci_Position CountByteCount(Sci_PositionU startPos, Sci_Position uncountedDigits, Accessor &styler)
248 {
249 	Sci_Position cnt;
250 	Sci_PositionU pos;
251 
252 	pos = startPos;
253 
254 	while (!IsNewline(styler.SafeGetCharAt(pos, '\n'))) {
255 		pos++;
256 	}
257 
258 	// number of digits in this line minus number of digits of uncounted fields
259 	cnt = static_cast<Sci_Position>(pos - startPos) - uncountedDigits;
260 
261 	// Prepare round up if odd (digit pair incomplete), this way the byte
262 	// count is considered to be valid if the checksum is incomplete.
263 	if (cnt >= 0) {
264 		cnt++;
265 	}
266 
267 	// digit pairs
268 	cnt /= 2;
269 
270 	return cnt;
271 }
272 
273 // Calculate the checksum of the record.
274 // <startPos> is the position of the first character of the starting digit
275 // pair, <cnt> is the number of digit pairs.
CalcChecksum(Sci_PositionU startPos,Sci_Position cnt,bool twosCompl,Accessor & styler)276 static int CalcChecksum(Sci_PositionU startPos, Sci_Position cnt, bool twosCompl, Accessor &styler)
277 {
278 	int cs = 0;
279 
280 	for (Sci_PositionU pos = startPos; pos < startPos + cnt; pos += 2) {
281 		int val = GetHexaChar(pos, styler);
282 
283 		if (val < 0) {
284 			return val;
285 		}
286 
287 		// overflow does not matter
288 		cs += val;
289 	}
290 
291 	if (twosCompl) {
292 		// low byte of two's complement
293 		return -cs & 0xFF;
294 	} else {
295 		// low byte of one's complement
296 		return ~cs & 0xFF;
297 	}
298 }
299 
300 // Get the position of the record "start" field (first character in line) in
301 // the record around position <pos>.
GetSrecRecStartPosition(Sci_PositionU pos,Accessor & styler)302 static Sci_PositionU GetSrecRecStartPosition(Sci_PositionU pos, Accessor &styler)
303 {
304 	while (styler.SafeGetCharAt(pos) != 'S') {
305 		pos--;
306 	}
307 
308 	return pos;
309 }
310 
311 // Get the value of the "byte count" field, it counts the number of bytes in
312 // the subsequent fields ("address", "data" and "checksum" fields).
GetSrecByteCount(Sci_PositionU recStartPos,Accessor & styler)313 static int GetSrecByteCount(Sci_PositionU recStartPos, Accessor &styler)
314 {
315 	int val;
316 
317 	val = GetHexaChar(recStartPos + 2, styler);
318 	if (val < 0) {
319 	       val = 0;
320 	}
321 
322 	return val;
323 }
324 
325 // Count the number of digit pairs for the "address", "data" and "checksum"
326 // fields in this record. Has to be equal to the "byte count" field value.
327 // If the record is too short, a negative count may be returned.
CountSrecByteCount(Sci_PositionU recStartPos,Accessor & styler)328 static Sci_Position CountSrecByteCount(Sci_PositionU recStartPos, Accessor &styler)
329 {
330 	return CountByteCount(recStartPos, 4, styler);
331 }
332 
333 // Get the size of the "address" field.
GetSrecAddressFieldSize(Sci_PositionU recStartPos,Accessor & styler)334 static int GetSrecAddressFieldSize(Sci_PositionU recStartPos, Accessor &styler)
335 {
336 	switch (styler.SafeGetCharAt(recStartPos + 1)) {
337 		case '0':
338 		case '1':
339 		case '5':
340 		case '9':
341 			return 2; // 16 bit
342 
343 		case '2':
344 		case '6':
345 		case '8':
346 			return 3; // 24 bit
347 
348 		case '3':
349 		case '7':
350 			return 4; // 32 bit
351 
352 		default:
353 			return 0;
354 	}
355 }
356 
357 // Get the type of the "address" field content.
GetSrecAddressFieldType(Sci_PositionU recStartPos,Accessor & styler)358 static int GetSrecAddressFieldType(Sci_PositionU recStartPos, Accessor &styler)
359 {
360 	switch (styler.SafeGetCharAt(recStartPos + 1)) {
361 		case '0':
362 			return SCE_HEX_NOADDRESS;
363 
364 		case '1':
365 		case '2':
366 		case '3':
367 			return SCE_HEX_DATAADDRESS;
368 
369 		case '5':
370 		case '6':
371 			return SCE_HEX_RECCOUNT;
372 
373 		case '7':
374 		case '8':
375 		case '9':
376 			return SCE_HEX_STARTADDRESS;
377 
378 		default: // handle possible format extension in the future
379 			return SCE_HEX_ADDRESSFIELD_UNKNOWN;
380 	}
381 }
382 
383 // Get the type of the "data" field content.
GetSrecDataFieldType(Sci_PositionU recStartPos,Accessor & styler)384 static int GetSrecDataFieldType(Sci_PositionU recStartPos, Accessor &styler)
385 {
386 	switch (styler.SafeGetCharAt(recStartPos + 1)) {
387 		case '0':
388 		case '1':
389 		case '2':
390 		case '3':
391 			return SCE_HEX_DATA_ODD;
392 
393 		case '5':
394 		case '6':
395 		case '7':
396 		case '8':
397 		case '9':
398 			return SCE_HEX_DATA_EMPTY;
399 
400 		default: // handle possible format extension in the future
401 			return SCE_HEX_DATA_UNKNOWN;
402 	}
403 }
404 
405 // Get the required size of the "data" field. Useless for block header and
406 // ordinary data records (type S0, S1, S2, S3), return the value calculated
407 // from the "byte count" and "address field" size in this case.
GetSrecRequiredDataFieldSize(Sci_PositionU recStartPos,Accessor & styler)408 static Sci_Position GetSrecRequiredDataFieldSize(Sci_PositionU recStartPos, Accessor &styler)
409 {
410 	switch (styler.SafeGetCharAt(recStartPos + 1)) {
411 		case '5':
412 		case '6':
413 		case '7':
414 		case '8':
415 		case '9':
416 			return 0;
417 
418 		default:
419 			return GetSrecByteCount(recStartPos, styler)
420 				- GetSrecAddressFieldSize(recStartPos, styler)
421 				- 1; // -1 for checksum field
422 	}
423 }
424 
425 // Get the value of the "checksum" field.
GetSrecChecksum(Sci_PositionU recStartPos,Accessor & styler)426 static int GetSrecChecksum(Sci_PositionU recStartPos, Accessor &styler)
427 {
428 	int byteCount;
429 
430 	byteCount = GetSrecByteCount(recStartPos, styler);
431 
432 	return GetHexaChar(recStartPos + 2 + byteCount * 2, styler);
433 }
434 
435 // Calculate the checksum of the record.
CalcSrecChecksum(Sci_PositionU recStartPos,Accessor & styler)436 static int CalcSrecChecksum(Sci_PositionU recStartPos, Accessor &styler)
437 {
438 	Sci_Position byteCount;
439 
440 	byteCount = GetSrecByteCount(recStartPos, styler);
441 
442 	// sum over "byte count", "address" and "data" fields (6..510 digits)
443 	return CalcChecksum(recStartPos + 2, byteCount * 2, false, styler);
444 }
445 
446 // Get the position of the record "start" field (first character in line) in
447 // the record around position <pos>.
GetIHexRecStartPosition(Sci_PositionU pos,Accessor & styler)448 static Sci_PositionU GetIHexRecStartPosition(Sci_PositionU pos, Accessor &styler)
449 {
450 	while (styler.SafeGetCharAt(pos) != ':') {
451 		pos--;
452 	}
453 
454 	return pos;
455 }
456 
457 // Get the value of the "byte count" field, it counts the number of bytes in
458 // the "data" field.
GetIHexByteCount(Sci_PositionU recStartPos,Accessor & styler)459 static int GetIHexByteCount(Sci_PositionU recStartPos, Accessor &styler)
460 {
461 	int val;
462 
463 	val = GetHexaChar(recStartPos + 1, styler);
464 	if (val < 0) {
465 	       val = 0;
466 	}
467 
468 	return val;
469 }
470 
471 // Count the number of digit pairs for the "data" field in this record. Has to
472 // be equal to the "byte count" field value.
473 // If the record is too short, a negative count may be returned.
CountIHexByteCount(Sci_PositionU recStartPos,Accessor & styler)474 static Sci_Position CountIHexByteCount(Sci_PositionU recStartPos, Accessor &styler)
475 {
476 	return CountByteCount(recStartPos, 11, styler);
477 }
478 
479 // Get the type of the "address" field content.
GetIHexAddressFieldType(Sci_PositionU recStartPos,Accessor & styler)480 static int GetIHexAddressFieldType(Sci_PositionU recStartPos, Accessor &styler)
481 {
482 	if (!PosInSameRecord(recStartPos, recStartPos + 7, styler)) {
483 		// malformed (record too short)
484 		// type cannot be determined
485 		return SCE_HEX_ADDRESSFIELD_UNKNOWN;
486 	}
487 
488 	switch (GetHexaChar(recStartPos + 7, styler)) {
489 		case 0x00:
490 			return SCE_HEX_DATAADDRESS;
491 
492 		case 0x01:
493 		case 0x02:
494 		case 0x03:
495 		case 0x04:
496 		case 0x05:
497 			return SCE_HEX_NOADDRESS;
498 
499 		default: // handle possible format extension in the future
500 			return SCE_HEX_ADDRESSFIELD_UNKNOWN;
501 	}
502 }
503 
504 // Get the type of the "data" field content.
GetIHexDataFieldType(Sci_PositionU recStartPos,Accessor & styler)505 static int GetIHexDataFieldType(Sci_PositionU recStartPos, Accessor &styler)
506 {
507 	switch (GetHexaChar(recStartPos + 7, styler)) {
508 		case 0x00:
509 			return SCE_HEX_DATA_ODD;
510 
511 		case 0x01:
512 			return SCE_HEX_DATA_EMPTY;
513 
514 		case 0x02:
515 		case 0x04:
516 			return SCE_HEX_EXTENDEDADDRESS;
517 
518 		case 0x03:
519 		case 0x05:
520 			return SCE_HEX_STARTADDRESS;
521 
522 		default: // handle possible format extension in the future
523 			return SCE_HEX_DATA_UNKNOWN;
524 	}
525 }
526 
527 // Get the required size of the "data" field. Useless for an ordinary data
528 // record (type 00), return the "byte count" in this case.
GetIHexRequiredDataFieldSize(Sci_PositionU recStartPos,Accessor & styler)529 static int GetIHexRequiredDataFieldSize(Sci_PositionU recStartPos, Accessor &styler)
530 {
531 	switch (GetHexaChar(recStartPos + 7, styler)) {
532 		case 0x01:
533 			return 0;
534 
535 		case 0x02:
536 		case 0x04:
537 			return 2;
538 
539 		case 0x03:
540 		case 0x05:
541 			return 4;
542 
543 		default:
544 			return GetIHexByteCount(recStartPos, styler);
545 	}
546 }
547 
548 // Get the value of the "checksum" field.
GetIHexChecksum(Sci_PositionU recStartPos,Accessor & styler)549 static int GetIHexChecksum(Sci_PositionU recStartPos, Accessor &styler)
550 {
551 	int byteCount;
552 
553 	byteCount = GetIHexByteCount(recStartPos, styler);
554 
555 	return GetHexaChar(recStartPos + 9 + byteCount * 2, styler);
556 }
557 
558 // Calculate the checksum of the record.
CalcIHexChecksum(Sci_PositionU recStartPos,Accessor & styler)559 static int CalcIHexChecksum(Sci_PositionU recStartPos, Accessor &styler)
560 {
561 	int byteCount;
562 
563 	byteCount = GetIHexByteCount(recStartPos, styler);
564 
565 	// sum over "byte count", "address", "type" and "data" fields (8..518 digits)
566 	return CalcChecksum(recStartPos + 1, 8 + byteCount * 2, true, styler);
567 }
568 
569 
570 // Get the value of the "record length" field, it counts the number of digits in
571 // the record excluding the percent.
GetTEHexDigitCount(Sci_PositionU recStartPos,Accessor & styler)572 static int GetTEHexDigitCount(Sci_PositionU recStartPos, Accessor &styler)
573 {
574 	int val = GetHexaChar(recStartPos + 1, styler);
575 	if (val < 0)
576 	       val = 0;
577 
578 	return val;
579 }
580 
581 // Count the number of digits in this record. Has to
582 // be equal to the "record length" field value.
CountTEHexDigitCount(Sci_PositionU recStartPos,Accessor & styler)583 static Sci_Position CountTEHexDigitCount(Sci_PositionU recStartPos, Accessor &styler)
584 {
585 	Sci_PositionU pos;
586 
587 	pos = recStartPos+1;
588 
589 	while (!IsNewline(styler.SafeGetCharAt(pos, '\n'))) {
590 		pos++;
591 	}
592 
593 	return static_cast<Sci_Position>(pos - (recStartPos+1));
594 }
595 
596 // Get the type of the "address" field content.
GetTEHexAddressFieldType(Sci_PositionU recStartPos,Accessor & styler)597 static int GetTEHexAddressFieldType(Sci_PositionU recStartPos, Accessor &styler)
598 {
599 	switch (styler.SafeGetCharAt(recStartPos + 3)) {
600 		case '6':
601 			return SCE_HEX_DATAADDRESS;
602 
603 		case '8':
604 			return SCE_HEX_STARTADDRESS;
605 
606 		default: // handle possible format extension in the future
607 			return SCE_HEX_ADDRESSFIELD_UNKNOWN;
608 	}
609 }
610 
611 // Get the value of the "checksum" field.
GetTEHexChecksum(Sci_PositionU recStartPos,Accessor & styler)612 static int GetTEHexChecksum(Sci_PositionU recStartPos, Accessor &styler)
613 {
614 	return GetHexaChar(recStartPos+4, styler);
615 }
616 
617 // Calculate the checksum of the record (excluding the checksum field).
CalcTEHexChecksum(Sci_PositionU recStartPos,Accessor & styler)618 static int CalcTEHexChecksum(Sci_PositionU recStartPos, Accessor &styler)
619 {
620 	Sci_PositionU pos = recStartPos +1;
621 	Sci_PositionU length = GetTEHexDigitCount(recStartPos, styler);
622 
623 	int cs = GetHexaNibble(styler.SafeGetCharAt(pos++));//length
624 	cs += GetHexaNibble(styler.SafeGetCharAt(pos++));//length
625 
626 	cs += GetHexaNibble(styler.SafeGetCharAt(pos++));//type
627 
628 	pos += 2;// jump over CS field
629 
630 	for (; pos <= recStartPos + length; ++pos) {
631 		int val = GetHexaNibble(styler.SafeGetCharAt(pos));
632 
633 		if (val < 0) {
634 			return val;
635 		}
636 
637 		// overflow does not matter
638 		cs += val;
639 	}
640 
641 	// low byte
642 	return cs & 0xFF;
643 
644 }
645 
ColouriseSrecDoc(Sci_PositionU startPos,Sci_Position length,int initStyle,WordList * [],Accessor & styler)646 static void ColouriseSrecDoc(Sci_PositionU startPos, Sci_Position length, int initStyle, WordList *[], Accessor &styler)
647 {
648 	StyleContext sc(startPos, length, initStyle, styler);
649 
650 	while (sc.More()) {
651 		Sci_PositionU recStartPos;
652 		Sci_Position reqByteCount;
653 		Sci_Position dataFieldSize;
654 		int byteCount, addrFieldSize, addrFieldType, dataFieldType;
655 		int cs1, cs2;
656 
657 		switch (sc.state) {
658 			case SCE_HEX_DEFAULT:
659 				if (sc.atLineStart && sc.Match('S')) {
660 					sc.SetState(SCE_HEX_RECSTART);
661 				}
662 				ForwardWithinLine(sc);
663 				break;
664 
665 			case SCE_HEX_RECSTART:
666 				recStartPos = sc.currentPos - 1;
667 				addrFieldType = GetSrecAddressFieldType(recStartPos, styler);
668 
669 				if (addrFieldType == SCE_HEX_ADDRESSFIELD_UNKNOWN) {
670 					sc.SetState(SCE_HEX_RECTYPE_UNKNOWN);
671 				} else {
672 					sc.SetState(SCE_HEX_RECTYPE);
673 				}
674 
675 				ForwardWithinLine(sc);
676 				break;
677 
678 			case SCE_HEX_RECTYPE:
679 			case SCE_HEX_RECTYPE_UNKNOWN:
680 				recStartPos = sc.currentPos - 2;
681 				byteCount = GetSrecByteCount(recStartPos, styler);
682 				reqByteCount = GetSrecAddressFieldSize(recStartPos, styler)
683 						+ GetSrecRequiredDataFieldSize(recStartPos, styler)
684 						+ 1; // +1 for checksum field
685 
686 				if (byteCount == CountSrecByteCount(recStartPos, styler)
687 						&& byteCount == reqByteCount) {
688 					sc.SetState(SCE_HEX_BYTECOUNT);
689 				} else {
690 					sc.SetState(SCE_HEX_BYTECOUNT_WRONG);
691 				}
692 
693 				ForwardWithinLine(sc, 2);
694 				break;
695 
696 			case SCE_HEX_BYTECOUNT:
697 			case SCE_HEX_BYTECOUNT_WRONG:
698 				recStartPos = sc.currentPos - 4;
699 				addrFieldSize = GetSrecAddressFieldSize(recStartPos, styler);
700 				addrFieldType = GetSrecAddressFieldType(recStartPos, styler);
701 
702 				sc.SetState(addrFieldType);
703 				ForwardWithinLine(sc, addrFieldSize * 2);
704 				break;
705 
706 			case SCE_HEX_NOADDRESS:
707 			case SCE_HEX_DATAADDRESS:
708 			case SCE_HEX_RECCOUNT:
709 			case SCE_HEX_STARTADDRESS:
710 			case SCE_HEX_ADDRESSFIELD_UNKNOWN:
711 				recStartPos = GetSrecRecStartPosition(sc.currentPos, styler);
712 				dataFieldType = GetSrecDataFieldType(recStartPos, styler);
713 
714 				// Using the required size here if possible has the effect that the
715 				// checksum is highlighted at a fixed position after this field for
716 				// specific record types, independent on the "byte count" value.
717 				dataFieldSize = GetSrecRequiredDataFieldSize(recStartPos, styler);
718 
719 				sc.SetState(dataFieldType);
720 
721 				if (dataFieldType == SCE_HEX_DATA_ODD) {
722 					for (int i = 0; i < dataFieldSize * 2; i++) {
723 						if ((i & 0x3) == 0) {
724 							sc.SetState(SCE_HEX_DATA_ODD);
725 						} else if ((i & 0x3) == 2) {
726 							sc.SetState(SCE_HEX_DATA_EVEN);
727 						}
728 
729 						if (!ForwardWithinLine(sc)) {
730 							break;
731 						}
732 					}
733 				} else {
734 					ForwardWithinLine(sc, dataFieldSize * 2);
735 				}
736 				break;
737 
738 			case SCE_HEX_DATA_ODD:
739 			case SCE_HEX_DATA_EVEN:
740 			case SCE_HEX_DATA_EMPTY:
741 			case SCE_HEX_DATA_UNKNOWN:
742 				recStartPos = GetSrecRecStartPosition(sc.currentPos, styler);
743 				cs1 = CalcSrecChecksum(recStartPos, styler);
744 				cs2 = GetSrecChecksum(recStartPos, styler);
745 
746 				if (cs1 != cs2 || cs1 < 0 || cs2 < 0) {
747 					sc.SetState(SCE_HEX_CHECKSUM_WRONG);
748 				} else {
749 					sc.SetState(SCE_HEX_CHECKSUM);
750 				}
751 
752 				ForwardWithinLine(sc, 2);
753 				break;
754 
755 			case SCE_HEX_CHECKSUM:
756 			case SCE_HEX_CHECKSUM_WRONG:
757 			case SCE_HEX_GARBAGE:
758 				// record finished or line too long
759 				sc.SetState(SCE_HEX_GARBAGE);
760 				ForwardWithinLine(sc);
761 				break;
762 
763 			default:
764 				// prevent endless loop in faulty state
765 				sc.SetState(SCE_HEX_DEFAULT);
766 				break;
767 		}
768 	}
769 	sc.Complete();
770 }
771 
ColouriseIHexDoc(Sci_PositionU startPos,Sci_Position length,int initStyle,WordList * [],Accessor & styler)772 static void ColouriseIHexDoc(Sci_PositionU startPos, Sci_Position length, int initStyle, WordList *[], Accessor &styler)
773 {
774 	StyleContext sc(startPos, length, initStyle, styler);
775 
776 	while (sc.More()) {
777 		Sci_PositionU recStartPos;
778 		int byteCount, addrFieldType, dataFieldSize, dataFieldType;
779 		int cs1, cs2;
780 
781 		switch (sc.state) {
782 			case SCE_HEX_DEFAULT:
783 				if (sc.atLineStart && sc.Match(':')) {
784 					sc.SetState(SCE_HEX_RECSTART);
785 				}
786 				ForwardWithinLine(sc);
787 				break;
788 
789 			case SCE_HEX_RECSTART:
790 				recStartPos = sc.currentPos - 1;
791 				byteCount = GetIHexByteCount(recStartPos, styler);
792 				dataFieldSize = GetIHexRequiredDataFieldSize(recStartPos, styler);
793 
794 				if (byteCount == CountIHexByteCount(recStartPos, styler)
795 						&& byteCount == dataFieldSize) {
796 					sc.SetState(SCE_HEX_BYTECOUNT);
797 				} else {
798 					sc.SetState(SCE_HEX_BYTECOUNT_WRONG);
799 				}
800 
801 				ForwardWithinLine(sc, 2);
802 				break;
803 
804 			case SCE_HEX_BYTECOUNT:
805 			case SCE_HEX_BYTECOUNT_WRONG:
806 				recStartPos = sc.currentPos - 3;
807 				addrFieldType = GetIHexAddressFieldType(recStartPos, styler);
808 
809 				sc.SetState(addrFieldType);
810 				ForwardWithinLine(sc, 4);
811 				break;
812 
813 			case SCE_HEX_NOADDRESS:
814 			case SCE_HEX_DATAADDRESS:
815 			case SCE_HEX_ADDRESSFIELD_UNKNOWN:
816 				recStartPos = sc.currentPos - 7;
817 				addrFieldType = GetIHexAddressFieldType(recStartPos, styler);
818 
819 				if (addrFieldType == SCE_HEX_ADDRESSFIELD_UNKNOWN) {
820 					sc.SetState(SCE_HEX_RECTYPE_UNKNOWN);
821 				} else {
822 					sc.SetState(SCE_HEX_RECTYPE);
823 				}
824 
825 				ForwardWithinLine(sc, 2);
826 				break;
827 
828 			case SCE_HEX_RECTYPE:
829 			case SCE_HEX_RECTYPE_UNKNOWN:
830 				recStartPos = sc.currentPos - 9;
831 				dataFieldType = GetIHexDataFieldType(recStartPos, styler);
832 
833 				// Using the required size here if possible has the effect that the
834 				// checksum is highlighted at a fixed position after this field for
835 				// specific record types, independent on the "byte count" value.
836 				dataFieldSize = GetIHexRequiredDataFieldSize(recStartPos, styler);
837 
838 				sc.SetState(dataFieldType);
839 
840 				if (dataFieldType == SCE_HEX_DATA_ODD) {
841 					for (int i = 0; i < dataFieldSize * 2; i++) {
842 						if ((i & 0x3) == 0) {
843 							sc.SetState(SCE_HEX_DATA_ODD);
844 						} else if ((i & 0x3) == 2) {
845 							sc.SetState(SCE_HEX_DATA_EVEN);
846 						}
847 
848 						if (!ForwardWithinLine(sc)) {
849 							break;
850 						}
851 					}
852 				} else {
853 					ForwardWithinLine(sc, dataFieldSize * 2);
854 				}
855 				break;
856 
857 			case SCE_HEX_DATA_ODD:
858 			case SCE_HEX_DATA_EVEN:
859 			case SCE_HEX_DATA_EMPTY:
860 			case SCE_HEX_EXTENDEDADDRESS:
861 			case SCE_HEX_STARTADDRESS:
862 			case SCE_HEX_DATA_UNKNOWN:
863 				recStartPos = GetIHexRecStartPosition(sc.currentPos, styler);
864 				cs1 = CalcIHexChecksum(recStartPos, styler);
865 				cs2 = GetIHexChecksum(recStartPos, styler);
866 
867 				if (cs1 != cs2 || cs1 < 0 || cs2 < 0) {
868 					sc.SetState(SCE_HEX_CHECKSUM_WRONG);
869 				} else {
870 					sc.SetState(SCE_HEX_CHECKSUM);
871 				}
872 
873 				ForwardWithinLine(sc, 2);
874 				break;
875 
876 			case SCE_HEX_CHECKSUM:
877 			case SCE_HEX_CHECKSUM_WRONG:
878 			case SCE_HEX_GARBAGE:
879 				// record finished or line too long
880 				sc.SetState(SCE_HEX_GARBAGE);
881 				ForwardWithinLine(sc);
882 				break;
883 
884 			default:
885 				// prevent endless loop in faulty state
886 				sc.SetState(SCE_HEX_DEFAULT);
887 				break;
888 		}
889 	}
890 	sc.Complete();
891 }
892 
FoldIHexDoc(Sci_PositionU startPos,Sci_Position length,int,WordList * [],Accessor & styler)893 static void FoldIHexDoc(Sci_PositionU startPos, Sci_Position length, int, WordList *[], Accessor &styler)
894 {
895 	Sci_PositionU endPos = startPos + length;
896 
897 	Sci_Position lineCurrent = styler.GetLine(startPos);
898 	int levelCurrent = SC_FOLDLEVELBASE;
899 	if (lineCurrent > 0)
900 		levelCurrent = styler.LevelAt(lineCurrent - 1);
901 
902 	Sci_PositionU lineStartNext = styler.LineStart(lineCurrent + 1);
903 	int levelNext = SC_FOLDLEVELBASE; // default if no specific line found
904 
905 	for (Sci_PositionU i = startPos; i < endPos; i++) {
906 		bool atEOL = i == (lineStartNext - 1);
907 		int style = styler.StyleAt(i);
908 
909 		// search for specific lines
910 		if (style == SCE_HEX_EXTENDEDADDRESS) {
911 			// extended addres record
912 			levelNext = SC_FOLDLEVELBASE | SC_FOLDLEVELHEADERFLAG;
913 		} else if (style == SCE_HEX_DATAADDRESS
914 			|| (style == SCE_HEX_DEFAULT
915 				&& i == (Sci_PositionU)styler.LineStart(lineCurrent))) {
916 			// data record or no record start code at all
917 			if (levelCurrent & SC_FOLDLEVELHEADERFLAG) {
918 				levelNext = SC_FOLDLEVELBASE + 1;
919 			} else {
920 				// continue level 0 or 1, no fold point
921 				levelNext = levelCurrent;
922 			}
923 		}
924 
925 		if (atEOL || (i == endPos - 1)) {
926 			styler.SetLevel(lineCurrent, levelNext);
927 
928 			lineCurrent++;
929 			lineStartNext = styler.LineStart(lineCurrent + 1);
930 			levelCurrent = levelNext;
931 			levelNext = SC_FOLDLEVELBASE;
932 		}
933 	}
934 }
935 
ColouriseTEHexDoc(Sci_PositionU startPos,Sci_Position length,int initStyle,WordList * [],Accessor & styler)936 static void ColouriseTEHexDoc(Sci_PositionU startPos, Sci_Position length, int initStyle, WordList *[], Accessor &styler)
937 {
938 	StyleContext sc(startPos, length, initStyle, styler);
939 
940 	while (sc.More()) {
941 		Sci_PositionU recStartPos;
942 		int digitCount, addrFieldType;
943 		int cs1, cs2;
944 
945 		switch (sc.state) {
946 			case SCE_HEX_DEFAULT:
947 				if (sc.atLineStart && sc.Match('%')) {
948 					sc.SetState(SCE_HEX_RECSTART);
949 				}
950 				ForwardWithinLine(sc);
951 				break;
952 
953 			case SCE_HEX_RECSTART:
954 
955 				recStartPos = sc.currentPos - 1;
956 
957 				if (GetTEHexDigitCount(recStartPos, styler) == CountTEHexDigitCount(recStartPos, styler)) {
958 					sc.SetState(SCE_HEX_BYTECOUNT);
959 				} else {
960 					sc.SetState(SCE_HEX_BYTECOUNT_WRONG);
961 				}
962 
963 				ForwardWithinLine(sc, 2);
964 				break;
965 
966 			case SCE_HEX_BYTECOUNT:
967 			case SCE_HEX_BYTECOUNT_WRONG:
968 				recStartPos = sc.currentPos - 3;
969 				addrFieldType = GetTEHexAddressFieldType(recStartPos, styler);
970 
971 				if (addrFieldType == SCE_HEX_ADDRESSFIELD_UNKNOWN) {
972 					sc.SetState(SCE_HEX_RECTYPE_UNKNOWN);
973 				} else {
974 					sc.SetState(SCE_HEX_RECTYPE);
975 				}
976 
977 				ForwardWithinLine(sc);
978 				break;
979 
980 			case SCE_HEX_RECTYPE:
981 			case SCE_HEX_RECTYPE_UNKNOWN:
982 				recStartPos = sc.currentPos - 4;
983 				cs1 = CalcTEHexChecksum(recStartPos, styler);
984 				cs2 = GetTEHexChecksum(recStartPos, styler);
985 
986 				if (cs1 != cs2 || cs1 < 0 || cs2 < 0) {
987 					sc.SetState(SCE_HEX_CHECKSUM_WRONG);
988 				} else {
989 					sc.SetState(SCE_HEX_CHECKSUM);
990 				}
991 
992 				ForwardWithinLine(sc, 2);
993 				break;
994 
995 
996 			case SCE_HEX_CHECKSUM:
997 			case SCE_HEX_CHECKSUM_WRONG:
998 				recStartPos = sc.currentPos - 6;
999 				addrFieldType = GetTEHexAddressFieldType(recStartPos, styler);
1000 
1001 				sc.SetState(addrFieldType);
1002 				ForwardWithinLine(sc, 9);
1003 				break;
1004 
1005 			case SCE_HEX_DATAADDRESS:
1006 			case SCE_HEX_STARTADDRESS:
1007 			case SCE_HEX_ADDRESSFIELD_UNKNOWN:
1008 				recStartPos = sc.currentPos - 15;
1009 				digitCount = GetTEHexDigitCount(recStartPos, styler) - 14;
1010 
1011 				sc.SetState(SCE_HEX_DATA_ODD);
1012 
1013 				for (int i = 0; i < digitCount; i++) {
1014 					if ((i & 0x3) == 0) {
1015 						sc.SetState(SCE_HEX_DATA_ODD);
1016 					} else if ((i & 0x3) == 2) {
1017 						sc.SetState(SCE_HEX_DATA_EVEN);
1018 					}
1019 
1020 					if (!ForwardWithinLine(sc)) {
1021 						break;
1022 					}
1023 				}
1024 				break;
1025 
1026 			case SCE_HEX_DATA_ODD:
1027 			case SCE_HEX_DATA_EVEN:
1028 			case SCE_HEX_GARBAGE:
1029 				// record finished or line too long
1030 				sc.SetState(SCE_HEX_GARBAGE);
1031 				ForwardWithinLine(sc);
1032 				break;
1033 
1034 			default:
1035 				// prevent endless loop in faulty state
1036 				sc.SetState(SCE_HEX_DEFAULT);
1037 				break;
1038 		}
1039 	}
1040 	sc.Complete();
1041 }
1042 
1043 LexerModule lmSrec(SCLEX_SREC, ColouriseSrecDoc, "srec", 0, NULL);
1044 LexerModule lmIHex(SCLEX_IHEX, ColouriseIHexDoc, "ihex", FoldIHexDoc, NULL);
1045 LexerModule lmTEHex(SCLEX_TEHEX, ColouriseTEHexDoc, "tehex", 0, NULL);
1046