-
-
Notifications
You must be signed in to change notification settings - Fork 36
2325 4480 pwd newline #4804
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
2325 4480 pwd newline #4804
Conversation
src/subshell/common.c
Outdated
* see ticket #4480. Make sure to wrap in time. See how large we can grow so that an | ||
* additional escaped byte and the closing string still fit. */ | ||
const int max_length = | ||
COOKED_MODE_BUFFER_SIZE - MAX (strlen (before_wrap), strlen (quote_cmd_end)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's better to save string lengths in the intermediate variables and then use them in g_string_append_len() instead of g_string_append() to avoid double calls of strlen().
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we care more about shaving off a few CPU cycles at a non-critical path, rather than preferring cleaner, easier to read code?
Moreover, the code also would become inconsistent with itself. E.g. quote_cmd_end
is used twice, so it speeds up things by a few nanoseconds to store its length. before_wrap
is likely used exactly once, with a small chance more than once. But after_wrap
is likely not used at all. So in order to shave off a few CPU cycles we shouldn't precompute its length?
I'd find it absolutely pointless to go into such mircooptimizations somewhere where it really doesn't matter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that optimizations are pointless, but I would still extract constants for readability. This would also make the line shorter and make it fit on one line.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've pushed an additional commit (to be squashed!!!) to show what the code would look like with precomputed lengths.
I honestly don't find it any more readable than the previous version; duplicating (kind of) variable names like g_string_append_len (ret, foobar, foobar_len)
are a source of error if you pass the wrong length, harder to verify that you didn't make this mistake, and is a visual clutter instead of just seeing foobar
once.
IMHO.
If you guys both prefer this variant then I'll squash this sommit with the previous one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i prefer the "optimized" version. having the lengths pre-calculated should make it easier to write code that doesn't buffer-overflow.
src/subshell/common.c
Outdated
g_string_append (ret, before_wrap); | ||
g_string_append_c (ret, '\n'); | ||
g_string_append (ret, after_wrap); | ||
line_length = strlen (after_wrap); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's better to swap these two lines:
line_length = strlen (after_wrap)
g_string_append_len (ret, after_wrap, line_length);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is it better? Currently the string append operations are next to each other. By swapping them, you'd break that flow.
Or is it because then you could use that as a length parameter to g_string_append_len; again, resulting in a visibly more inconsisent code for us humans, and an absolutely unmeasurable speedup? Let's go for more readable, more consistent code please. Let me know please why you think your proposal would be more readable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I actually have no preference one way or the other here...
src/subshell/common.c
Outdated
g_string_append (ret, before_wrap); | ||
g_string_append_c (ret, '\n'); | ||
g_string_append (ret, after_wrap); | ||
line_length = strlen (after_wrap); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The same:
line_length = strlen (after_wrap)
g_string_append_len (ret, after_wrap, line_length);
ceeb70e
to
d6b260d
Compare
Oops, I've accidentally pushed the branch into the main mc repo; I didn't mean that. Sorry for that! You can remove that branch if you wish. |
Nevermind, I've deleted it. Sorry for the noise. |
There's a regression with tcsh: Previously it could enter directories with a non-alphanumeric UTF-8 symbol (e.g. heart Let me play around a little bit with tcsh to see if I can fix this. Let's hold off this PR for now. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
3nd commit msg: typo in 'platforms'
src/subshell/common.c
Outdated
* cd "`printf '%b' 'ABC\0nnnDEF\0nnnXYZ'`" | ||
* Enter any directory safely, no matter what special bytes its name contains (special shell | ||
* characters, control characters, non-printable characters, invalid UTF-8 etc.). | ||
* NOTE: Treat directory name an untrusted data, don't allow it to cause executing any commands in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"as untrusted"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just moved this text away by a few lines :) anyway, fixing.
src/subshell/common.c
Outdated
* | ||
* Wrapping to new line with a trailing backslash outside of the innermost single quotes. | ||
*/ | ||
quote_cmd_start = " _mc_newdir_=\"`printf '%b_' '"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the assigned value does not need to be quoted, and for legacy shells it even should not be.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, fixing.
src/subshell/common.c
Outdated
g_string_append (ret, "./"); | ||
|
||
// Copy the beginning of the command to the buffer | ||
g_string_append (ret, quote_cmd_start); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
side note: this happening after the ./ append is plain bogus.
(fix should be in a separate commit.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have also noticed this, but I just don't want to begin fixing every minor detail that I don't fully like :) But now that you've pointed it out too, I'll fix it.
But, sorry, I won't create a new commit for every tiny change I make, I just find that nonsense and super counterproductive. It's a somewhat bigger rework of that method, including everything that goes into it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, no... wait... now that I've moved the cd
string prefix into this method (which I had to do because of tcsh's variable construction), I have to swap the order, the one in my change was straight wrong.
src/subshell/common.c
Outdated
* see ticket #4480. Make sure to wrap in time. See how large we can grow so that an | ||
* additional escaped byte and the closing string still fit. */ | ||
const int max_length = | ||
COOKED_MODE_BUFFER_SIZE - MAX (strlen (before_wrap), strlen (quote_cmd_end)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i prefer the "optimized" version. having the lengths pre-calculated should make it easier to write code that doesn't buffer-overflow.
src/subshell/common.c
Outdated
* see ticket #4480. Make sure to wrap in time. See how large we can grow so that an | ||
* additional line wrapping or closing string still fits. */ | ||
const int max_length = | ||
COOKED_MODE_BUFFER_SIZE - MAX (strlen (before_wrap), strlen (quote_cmd_end)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it would be kinda more elegant to wrap the trailer separately if the situation occurs, but the extra code is probably not worth it ...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i prefer the "optimized" version. having the lengths pre-calculated should make it easier to write code that doesn't buffer-overflow.
Looks like it's 3:1 (all 3 of you reviewers against me), so I'll change the code (squash the followup commit), but I'm really curious: Could you please provide an example where a pre-computed length would save you from a buffer overrun?
I have shown you that it becomes easy to use the wrong length, e.g.
g_string_append_len (ret, foobar1, foobar1_len);
g_string_append_len (ret, quux2, quux1_len);
g_string_append_len (ret, loremipsum3, lorempisum3_len);
Not only is it harder to read, but would you spot the bug if you weren't looking for it? How is it any safer than writing
g_string_append (ret, foobar1);
g_string_append (ret, quux2);
g_string_append (ret, loremipsum3);
?
it would be kinda more elegant to wrap the trailer separately if the situation occurs, but the extra code is probably not worth it ...
Could you please elaborate? I don't get what you're thinking of here. Even though you're not asking me to change the code, I'm curious :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if you assign the lengths and actually use them to pre-calculate and check the buffer, it's easier to make sure that you're actually working with the same numbers. the mismatch risk is real, but i personally worry about it less, because i have annoyingly good visual pattern recognition.
the length of the final cd command is currently subtracted from the buffer size, so it always fits. but it would be possible to ensure that only the line termination fits, and put the cd on its own line if necessary.
… the subshell If the subshell writes the working directory slowly, previously we could read its beginning and stop there. Signed-off-by: Egmont Koblinger <[email protected]>
This piece of code was never live in mc. It would work around a BusyBox bug that was fixed in 2012. Signed-off-by: Egmont Koblinger <[email protected]>
57e9ac6
to
d267252
Compare
Back to tcsh: I think I can do either of these two things:
So it's either-or: invalid UTF-8 but no newlines, or newlines but not invalid UTF-8. And no, I'm not going to implement both and pick runtime so that we only fail if a path contains both :) Which one do you guys vote for? |
tcsh: I was wrong, command substitution isn't binary safe either. So, without terrible hacks, I can get newline working but not invalid UTF-8. To get invalid UTF-8 working, I think this would do it:
and when a command completes and tcsh sends it working directory to mc, invoke the external I'm not gonna do this, it's just not worth it. |
My thinking is that if I had to choose, I'd take newlines. |
…ng a directory with special characters Handle trailing '\n' character in the directory name. Make sure to construct the cd command in physical lines no longer than 250 bytes so that we don't hit the small limit of the kernel's cooked mode tty buffer size on some platforms. tcsh still has problems entering directories with special characters (including invalid UTF-8) in their name. Other shells are now believed to handle any directory name properly. Signed-off-by: Egmont Koblinger <[email protected]>
…ibyte UTF-8 Don't escape safe shell characters commonly used in paths, such as '/', '.', '-' and '_'. Don't escape multibyte UTF-8 characters. Escaping each byte separately in string assignments doesn't work in tcsh. The previous commit introduces a regression here: tcsh cannot enter directories whose name is valid UTF-8 but contains non-alphanumeric UTF-8 characters. It used to work because printf would glue them together correctly, but we no longer use printf and command substitution because that breaks newlines. Signed-off-by: Egmont Koblinger <[email protected]>
d267252
to
eefcf8c
Compare
Yup I'm going with newlines. New commit pushed to fix regression with tcsh. How confident are we that placing unquoted 128..255 bytes in the command line is safe in every shell, they don't have any special meaning in the shell? |
Proposed changes
More robust reading of pwd, namely:
Checklist
👉 Our coding style can be found here: https://midnight-commander.org/coding-style/ 👈
git commit --amend -s
make indent && make check
)