Hey there! It's me again. I hope you don't mind if I dust this thread off?
Quite a bit of time has passed since 4coder November, and while I immediately started updating my customizations (and taking care of lots of TODOs in the process), other side projects, Opus Magnum, university and life in general kind of got in the way, which is why I'm only posting now.
First of all, I must say that I was a bit disappointed to find that neither
directory_cd, nor
buffer_set_setting were fixed. It's not that big a deal, since I have worked around both of them before, but since (I think?) you mentioned at some point during November that you had crossed everything off your list, I figured I'd bring those up again.
Second, today I chased down a bug in my find and replace commands into the
buffer_seek_string_forward and
buffer_seek_string_insensitive_forward functions, which reported a match where there was none, for a quite nasty reason; using the former as an example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51 | static void
buffer_seek_string_forward(Application_Links *app, Buffer_Summary *buffer, int32_t pos, int32_t end, char *str, int32_t size, int32_t *result){
char read_buffer[512];
if (buffer->size > end){
*result = buffer->size;
}
else{
*result = end;
}
if (size > 0 && size <= sizeof(read_buffer)){
if (buffer->exists){
String read_str = make_fixed_width_string(read_buffer);
String needle_str = make_string(str, size);
char first_char = str[0];
read_str.size = size;
char chunk[1024];
Stream_Chunk stream = {0};
stream.max_end = end;
if (init_stream_chunk(&stream, app, buffer, pos, chunk, sizeof(chunk))){
int32_t still_looping = 1;
do{
for(; pos < stream.end; ++pos){
char at_pos = stream.data[pos];
if (at_pos == first_char){
buffer_read_range(app, buffer, pos, pos+size, read_buffer);
if (match_ss(needle_str, read_str)){
*result = pos;
goto finished;
}
}
}
still_looping = forward_stream_chunk(&stream);
}while (still_looping);
}
}
if (end == 0){
*result = buffer->size;
}
else{
*result = end;
}
finished:;
}
}
|
Basically, it comes down to the
read_buffer not being cleared, the unconditional
read_str.size = size in line 18, and the
buffer_read_range call in line 30 not being checked for success, all conspiring to allow the
match_ss call in line 31 to read uninitialized memory, which under specific circumstances (depending on what was called before
buffer_seek_string, and the value of
pos relative to
buffer->size), will actually return true.
(If you'd like, I'll sit down and figure out a good way to repro this tomorrow - I just wanted to get the report out quickly.)
Now, it's pretty easy to prevent this from happening by calling the function with
end = buffer->size - needle_str->size, rather than just
end = buffer->size, but that requirement is not documented anywhere, and even if it were, this mistake is just too easy to make, and too hard to track down, in my opinion. I'd recommend just placing an early return at the top of the function, something along the lines of
| if (pos > end - size) {
*result = end;
return;
}
|
I don't wanna be all negative, though! One of the side projects that got me side-tracked was playing around with Go. Naturally, I used 4coder for this, and pretty quickly ended up annoyed with the lack of support for the language, so I quickly whipped up this language header:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61 | /******************************************************************************
Author: Tristan Dannenberg
Notice: No warranty is offered or implied; use this code at your own risk.
*******************************************************************************
LICENSE
This software is dual-licensed to the public domain and under the following
license: you are granted a perpetual, irrevocable license to copy, modify,
publish, and distribute this file as you see fit.
*******************************************************************************
This file provides a parse context for golang syntax highlighting.
******************************************************************************/
#if !defined(FTLD_LANGUAGE_GO_HPP)
#define FTLD_LANGUAGE_GO_HPP
static Parse_Context_ID tld_parse_context_language_golang;
#define PSAT(s, t) {s, sizeof(s)-1, t}
static void
tld_init_language_golang(Application_Links *app){
if (tld_parse_context_language_golang != 0) return;
Parser_String_And_Type kw[] = {
PSAT("true", CPP_TOKEN_BOOLEAN_CONSTANT),
PSAT("false", CPP_TOKEN_BOOLEAN_CONSTANT),
PSAT("chan", CPP_TOKEN_KEY_OTHER),
PSAT("const", CPP_TOKEN_KEY_OTHER),
PSAT("default", CPP_TOKEN_KEY_OTHER),
PSAT("func", CPP_TOKEN_KEY_OTHER),
PSAT("import", CPP_TOKEN_KEY_OTHER),
PSAT("interface", CPP_TOKEN_KEY_OTHER),
PSAT("map", CPP_TOKEN_KEY_OTHER),
PSAT("nil", CPP_TOKEN_KEY_OTHER),
PSAT("package", CPP_TOKEN_KEY_OTHER),
PSAT("range", CPP_TOKEN_KEY_OTHER),
PSAT("return", CPP_TOKEN_KEY_OTHER),
PSAT("struct", CPP_TOKEN_KEY_OTHER),
PSAT("type", CPP_TOKEN_KEY_OTHER),
PSAT("var", CPP_TOKEN_KEY_OTHER),
PSAT("break", CPP_TOKEN_KEY_CONTROL_FLOW),
PSAT("continue", CPP_TOKEN_KEY_CONTROL_FLOW),
PSAT("case", CPP_TOKEN_KEY_CONTROL_FLOW),
PSAT("defer", CPP_TOKEN_KEY_CONTROL_FLOW),
PSAT("else", CPP_TOKEN_KEY_CONTROL_FLOW),
PSAT("fallthrough", CPP_TOKEN_KEY_CONTROL_FLOW),
PSAT("for", CPP_TOKEN_KEY_CONTROL_FLOW),
PSAT("go", CPP_TOKEN_KEY_CONTROL_FLOW),
PSAT("goto", CPP_TOKEN_KEY_CONTROL_FLOW),
PSAT("if", CPP_TOKEN_KEY_CONTROL_FLOW),
PSAT("select", CPP_TOKEN_KEY_CONTROL_FLOW),
PSAT("switch", CPP_TOKEN_KEY_CONTROL_FLOW),
};
tld_parse_context_language_golang = create_parse_context(app, kw, ArrayCount(kw), 0, 0);
}
#undef PSAT
#endif
|
Having syntax highlighting was nice, but I had to disable auto-indenting files on save, since 4coder_auto_indent.cpp gets confused with Go's lack of semicolons. I got fed up with having to manually indent code like a peasant just as quickly, though, so today I made some modifications to the indenter, both in the
get_indentation_marks function:
| // NOTE: This goes at the top of the function, with other initialization code
bool tld_golang_indentation = false;
Parse_Context_ID parse_context_id = 0;
buffer_get_setting(app, buffer, BufferSetting_ParserContext, (int32_t *) &parse_context_id);
if (parse_context_id == tld_parse_context_language_golang) {
tld_golang_indentation = true;
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 | // NOTE: This goes in the middle of the function, where `statement_complete` is determined
if (!has_prev_usable_token){
statement_complete = true;
}
else{
if (prev_usable_token.type == CPP_TOKEN_BRACKET_OPEN ||
prev_usable_token.type == CPP_TOKEN_BRACE_OPEN ||
prev_usable_token.type == CPP_TOKEN_BRACE_CLOSE ||
prev_usable_token.type == CPP_TOKEN_SEMICOLON ||
prev_usable_token.type == CPP_TOKEN_COLON ||
prev_usable_token.type == CPP_TOKEN_COMMA ||
// NOTE: TLD modifications begin here:
(tld_golang_indentation && (
prev_usable_token.type == CPP_TOKEN_BOOLEAN_CONSTANT ||
prev_usable_token.type == CPP_TOKEN_STRING_CONSTANT ||
prev_usable_token.type == CPP_TOKEN_IDENTIFIER ||
prev_usable_token.type == CPP_TOKEN_INTEGER_CONSTANT ||
prev_usable_token.type == CPP_TOKEN_FLOATING_CONSTANT ||
prev_usable_token.type == CPP_TOKEN_CHARACTER_CONSTANT ||
prev_usable_token.type == CPP_TOKEN_PARENTHESE_CLOSE ||
prev_usable_token.type == CPP_TOKEN_BRACKET_CLOSE)))
// NOTE: TLD modifications end here
{
statement_complete = true;
}
}
|
I figured you might like to integrate that into the default customization layer. Not that this is entirely altruistic; these kinds of modifications are enormously annoying to maintain in the face of 4coder updates, and it'd be great if you'd audit them for me, since you wrote the indenter, and I didn't spend too much time actually reading your code. I did test this stuff, obviously, and it seems to work fine, but still.
I'll push my other customization code to Github when I'm done upgrading the final command pack. Until then, I'll just apologize for the wall of text and thank you for your efforts and attention.
Best regards~