-
Notifications
You must be signed in to change notification settings - Fork 140
mingw: Fix crash in needs_hiding() #414
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -355,15 +355,19 @@ static inline int needs_hiding(const char *path) | |
if (!*path) | ||
return 0; | ||
|
||
for (basename = path; *path; path++) | ||
if (is_dir_sep(*path)) { | ||
do { | ||
path++; | ||
} while (is_dir_sep(*path)); | ||
/* ignore trailing slashes */ | ||
if (*path) | ||
basename = path; | ||
} | ||
/* find last non-empty path component */ | ||
for (basename = path; *path; path++) { | ||
if (!is_dir_sep(*path)) | ||
continue; | ||
|
||
if (!path[1]) | ||
break; | ||
|
||
if (is_dir_sep(path[1])) | ||
continue; | ||
|
||
basename = path + 1; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's way too clever for my liking. I had to contort my brain a little to see that you are essentially skipping chains of directory separators in a convoluted way. So let me propose something completely different instead, something that should be uncontroversial: basename = find_last_dir_sep(path);
if (basename)
basename++; There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I admit that switching from one algorithm to another is a bit hard to understand. Reading my algorithm with a fresh eye is probably easier then reading old code, though. But yes, this can vary from reader to reader. The new code you suggested will fail if path ends with slash - it will not achieve the goal of finding last non-empty component. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The idea of my algorithm is that it inspects every slash and decides yes/no. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Good point.
I will readily admit that I find your code harder to follow. In case of a chain of slashes (as in And I had another look at your version vs the original version with the added It is more obvious because it saves me brain cycles: we're looking for a directory separator, okay. If we found one, we skip over any directory separators. Okay. Now we see whether there is anything left, and if so, assign It requires only linear thinking. In your version, you artificially avoid one indentation level by starting with an See where this is going? It requires quite a bit of twisting, and that was not the case with the original version. Now, your version could be improved by skipping the third /* find last non-empty path component */
for (basename = path; *path; path++)
if (is_dir_sep(*path) && path[1] && !is_dir_sep(path[1]))
basename = path + 1; I do not particularly like this condition ( for (basename = path; *path; )
/*
* A basename
* - starts after a directory separator,
* - is not empty, and
* - does not begin with a directory separator
*/
if (is_dir_sep(*(path++)) && *path && !is_dir_sep(*path))
basename = path; Oh, and I just noticed that you mix spaces and tabs for indentation. Please don't do that. Git uses 8-column tabs only, with spaces for the final part of the indentation if required. BTW have you noticed that all discussed versions mishandle a path that consists only of slashes? Even on Windows, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In short, I think that this is a much better version: static inline int needs_hiding(const char *path)
{
const char *basename = NULL;
if (hide_dotfiles == HIDE_DOTFILES_FALSE)
return 0;
/* We cannot use basename(), as it would remove trailing slashes */
win32_skip_dos_drive_prefix((char **)&path);
if (!is_dir_sep(*path))
basename = path;
while (*path)
/*
* A basename
* - starts after a directory separator,
* - is not empty, and
* - does not begin with a directory separator
*/
if (is_dir_sep(*(path++) && *path && !is_dir_sep(*path)))
basename = path;
if (!basename)
return 0;
if (hide_dotfiles == HIDE_DOTFILES_TRUE)
return *basename == '.';
assert(hide_dotfiles == HIDE_DOTFILES_DOTGITONLY);
return !strncasecmp(".git", basename, 4) &&
(!basename[4] || is_dir_sep(basename[4]));
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The disagreement is negligible, so I'm fine with whatever code you like more. It's a pity the patch is no longer mine. Boasting rights, you know. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Sorry, I meant to give you credit via that footer. Would you like a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes please :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Right, but there is also:
However, this is pretty much one of the most interesting aspects of the algorithm, and I find it harder to reason about the correctness of your algorithm in that respect than about my version.
But it does: the Instead, what we would want is
It happens to work because There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK :) We just have slightly different preferences. |
||
} | ||
|
||
if (hide_dotfiles == HIDE_DOTFILES_TRUE) | ||
return *basename == '.'; | ||
|
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.
How about an
else break;
here? That would make your patch substantially simpler.And yes, this is a bug...
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.
Nested loops with single counter are prone to bugs like the one I'm fixing. That's why I decided to also remove the surface where problems arise. If you really want to, I'll change it, but I'd rather submit current patch.
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 want to change the shape of the function, that's fine, but please do that as a separate patch that does not claim to fix a bug at the same time. It is hard to spot bugs when a patch tries to do too many things, and I have to admit that I could not convince myself even after reading the patch for a full 2 minutes that it does not introduce a regression...