From: Johannes Schindelin <hidden> Date: 2016-06-15 23:06:44
When there is no `libgen.h` to our disposal, we miss the `dirname()`
function.
So far, we only had one user of that function: credential-cache--daemon
(which was only compiled when Unix sockets are available, anyway). But
now we also have `builtin/am.c` as user, so we need it.
Since `dirname()` is a sibling of `basename()`, we simply put our very
own `gitdirname()` implementation next to `gitbasename()` and use it
if `NO_LIBGEN_H` has been set.
Signed-off-by: Johannes Schindelin <redacted>
---
I stumbled over the compile warning when upgrading Git for Windows
to 2.6.0. There was a left-over NO_LIBGEN_H=YesPlease (which we
no longer need in Git for Windows 2.x), but it did point to the
fact that we use `dirname()` in builtin/am.c now, so we better
have a fall-back implementation for platforms without libgen.h.
I tested this implementation a bit, but I still would appreciate
a few eye-balls to go over it.
compat/basename.c | 26 ++++++++++++++++++++++++++
git-compat-util.h | 2 ++
2 files changed, 28 insertions(+)
@@ -13,3 +13,29 @@ char *gitbasename (char *path)}return(char*)base;}++char*gitdirname(char*path)+{+char*p=path,*slash,c;++/* Skip over the disk name in MSDOS pathnames. */+if(has_dos_drive_prefix(p))+p+=2;+/* POSIX.1-2001 says dirname("/") should return "/" */+slash=is_dir_sep(*p)?++p:NULL;+while((c=*(p++)))+if(is_dir_sep(c)){+char*tentative=p-1;++/* POSIX.1-2001 says to ignore trailing slashes */+while(is_dir_sep(*p))+p++;+if(*p)+slash=tentative;+}++if(!slash)+return".";+*slash='\0';+returnpath;+}
From: Ramsay Jones <hidden> Date: 2016-06-15 23:06:44
Hi Johannes,
On 30/09/15 15:50, Johannes Schindelin wrote:
When there is no `libgen.h` to our disposal, we miss the `dirname()`
function.
So far, we only had one user of that function: credential-cache--daemon
(which was only compiled when Unix sockets are available, anyway). But
now we also have `builtin/am.c` as user, so we need it.
Yes, many moons ago (on my old 32-bit laptop) when I was still 'working'
with MinGW I noticed this same thing while looking into providing a win32
emulation of unix sockets. So, I had to look into this at the same time.
Since this didn't progress, I didn't mention the libgen issue.
Anyway, I still have a 'test-libgen.c' file (attached) from back then that
contains some tests. I don't quite recall what the final state of this
code was, but it was intended to test _existing_ libgen implementations
as well as provide a 'git' version which would work on MinGW, cygwin and
linux. Note that some of the existing implementations didn't all agree on
what the tests should report! I don't remember if I looked at the POSIX
spec or not.
So, I don't know how useful it will be - if nothing else, there are some
tests! :-D
HTH
Ramsay Jones
quoted hunk
Since `dirname()` is a sibling of `basename()`, we simply put our very
own `gitdirname()` implementation next to `gitbasename()` and use it
if `NO_LIBGEN_H` has been set.
Signed-off-by: Johannes Schindelin <redacted>
---
I stumbled over the compile warning when upgrading Git for Windows
to 2.6.0. There was a left-over NO_LIBGEN_H=YesPlease (which we
no longer need in Git for Windows 2.x), but it did point to the
fact that we use `dirname()` in builtin/am.c now, so we better
have a fall-back implementation for platforms without libgen.h.
I tested this implementation a bit, but I still would appreciate
a few eye-balls to go over it.
compat/basename.c | 26 ++++++++++++++++++++++++++
git-compat-util.h | 2 ++
2 files changed, 28 insertions(+)
@@ -13,3 +13,29 @@ char *gitbasename (char *path)}return(char*)base;}++char*gitdirname(char*path)+{+char*p=path,*slash,c;++/* Skip over the disk name in MSDOS pathnames. */+if(has_dos_drive_prefix(p))+p+=2;+/* POSIX.1-2001 says dirname("/") should return "/" */+slash=is_dir_sep(*p)?++p:NULL;+while((c=*(p++)))+if(is_dir_sep(c)){+char*tentative=p-1;++/* POSIX.1-2001 says to ignore trailing slashes */+while(is_dir_sep(*p))+p++;+if(*p)+slash=tentative;+}++if(!slash)+return".";+*slash='\0';+returnpath;+}
From: Johannes Schindelin <hidden> Date: 2016-06-15 23:07:40
Hi Ramsay,
On Wed, 30 Sep 2015, Ramsay Jones wrote:
On 30/09/15 15:50, Johannes Schindelin wrote:
quoted
When there is no `libgen.h` to our disposal, we miss the `dirname()`
function.
So far, we only had one user of that function:
credential-cache--daemon (which was only compiled when Unix sockets
are available, anyway). But now we also have `builtin/am.c` as user,
so we need it.
Yes, many moons ago (on my old 32-bit laptop) when I was still 'working'
with MinGW I noticed this same thing while looking into providing a win32
emulation of unix sockets. So, I had to look into this at the same time.
Since this didn't progress, I didn't mention the libgen issue.
Anyway, I still have a 'test-libgen.c' file (attached) from back then that
contains some tests.
Awesome. Thank you! I integrated the tests back into test-path-utils.c
(from where the framework clearly came) and made it part of the regression
test suite in the upcoming v2.
Ciao,
Dscho
From: Johannes Schindelin <hidden> Date: 2016-06-15 23:07:40
Junio Hamano pointed out that there is an implicit assumption in pretty
much all the code calling has_dos_drive_prefix(): it assumes that the
DOS drive prefix is always two bytes long.
While this assumption is pretty safe, we can still make the code more
readable and less error-prone by introducing a function that skips the
DOS drive prefix safely.
While at it, we change the has_dos_drive_prefix() return value: it now
returns the number of bytes to be skipped if there is a DOS drive prefix.
Signed-off-by: Johannes Schindelin <redacted>
---
compat/basename.c | 4 +---
compat/mingw.c | 14 +++++---------
compat/mingw.h | 10 +++++++++-
git-compat-util.h | 8 ++++++++
path.c | 14 +++++---------
5 files changed, 28 insertions(+), 22 deletions(-)
@@ -4,9 +4,7 @@char*gitbasename(char*path){constchar*base;-/* Skip over the disk name in MSDOS pathnames. */-if(has_dos_drive_prefix(path))-path+=2;+skip_dos_drive_prefix(&path);for(base=path;*path;path++){if(is_dir_sep(*path))base=path+1;
From: Johannes Schindelin <hidden> Date: 2016-06-15 23:07:40
When there is no `libgen.h` to our disposal, we miss the `dirname()`
function.
So far, we only had one user of that function: credential-cache--daemon
(which was only compiled when Unix sockets are available, anyway). But
now we also have `builtin/am.c` as user, so we need it.
Since `dirname()` is a sibling of `basename()`, we simply put our very
own `gitdirname()` implementation next to `gitbasename()` and use it
if `NO_LIBGEN_H` has been set.
Signed-off-by: Johannes Schindelin <redacted>
---
compat/basename.c | 44 ++++++++++++++++++++++++++++++++++++++++++++
git-compat-util.h | 2 ++
2 files changed, 46 insertions(+)
From: Johannes Schindelin <hidden> Date: 2016-06-15 23:07:40
Unfortunately, some libgen implementations yield outcomes different from
what Git expects. For example, mingw-w64-crt provides a basename()
function, that shortens `path0/` to `path`!
So let's verify that the basename() and dirname() functions we use conform
to what Git expects.
Derived-from-code-by: Ramsay Jones [off-list ref]
Signed-off-by: Johannes Schindelin <redacted>
---
t/t0060-path-utils.sh | 3 +
test-path-utils.c | 168 ++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 171 insertions(+)
@@ -39,6 +39,168 @@ static void normalize_argv_string(const char **var, const char *input)die("Bad value: %s\n",input);}+structtest_data{+char*from;/* input: transform from this ... */+char*to;/* output: ... to this. */+};++staticinttest_function(structtest_data*data,char*(*func)(char*input),+constchar*funcname)+{+intfailed=0,i;+staticcharbuffer[1024];+char*to;++for(i=0;data[i].to;i++){+if(!data[i].from)+to=func(NULL);+else{+strcpy(buffer,data[i].from);+to=func(buffer);+}+if(strcmp(to,data[i].to)){+error("FAIL: %s(%s) => '%s' != '%s'\n",+funcname,data[i].from,to,data[i].to);+failed++;+}+}+return!!failed;+}++staticstructtest_databasename_data[]={+/* --- POSIX type paths --- */+{NULL,"."},+{"","."},+{".","."},+{"..",".."},+{"/","/"},+#if defined(__CYGWIN__) && !defined(NO_LIBGEN_H)+{"//","//"},+{"///","//"},+{"////","//"},+#else+{"//","/"},+{"///","/"},+{"////","/"},+#endif+{"usr","usr"},+{"/usr","usr"},+{"/usr/","usr"},+{"/usr//","usr"},+{"/usr/lib","lib"},+{"usr/lib","lib"},+{"usr/lib///","lib"},++#if defined(__MINGW32__) || defined(_MSC_VER)++/* --- win32 type paths --- */+{"\\usr","usr"},+{"\\usr\\","usr"},+{"\\usr\\\\","usr"},+{"\\usr\\lib","lib"},+{"usr\\lib","lib"},+{"usr\\lib\\\\\\","lib"},+{"C:/usr","usr"},+{"C:/usr","usr"},+{"C:/usr/","usr"},+{"C:/usr//","usr"},+{"C:/usr/lib","lib"},+{"C:usr/lib","lib"},+{"C:usr/lib///","lib"},+{"C:","."},+{"C:a","a"},+{"C:/","/"},+{"C:///","/"},+#if defined(NO_LIBGEN_H)+{"\\","\\"},+{"\\\\","\\"},+{"\\\\\\","\\"},+#else++/* win32 platform variations: */+#if defined(__MINGW32__)+{"\\","/"},+{"\\\\","/"},+{"\\\\\\","/"},+#endif++#if defined(_MSC_VER)+{"\\","\\"},+{"\\\\","\\"},+{"\\\\\\","\\"},+#endif++#endif+#endif+{NULL,"."},+{NULL,NULL}+};++staticstructtest_datadirname_data[]={+/* --- POSIX type paths --- */+{NULL,"."},+{"","."},+{".","."},+{"..","."},+{"/","/"},+{"//","//"},+#if defined(__CYGWIN__) && !defined(NO_LIBGEN_H)+{"///","//"},+{"////","//"},+#else+{"///","/"},+{"////","/"},+#endif+{"usr","."},+{"/usr","/"},+{"/usr/","/"},+{"/usr//","/"},+{"/usr/lib","/usr"},+{"usr/lib","usr"},+{"usr/lib///","usr"},++#if defined(__MINGW32__) || defined(_MSC_VER)++/* --- win32 type paths --- */+{"\\","\\"},+{"\\\\","\\\\"},+{"\\usr","\\"},+{"\\usr\\","\\"},+{"\\usr\\\\","\\"},+{"\\usr\\lib","\\usr"},+{"usr\\lib","usr"},+{"usr\\lib\\\\\\","usr"},+{"C:a","C:."},+{"C:/","C:/"},+{"C:///","C:/"},+{"C:/usr","C:/"},+{"C:/usr/","C:/"},+{"C:/usr//","C:/"},+{"C:/usr/lib","C:/usr"},+{"C:usr/lib","C:usr"},+{"C:usr/lib///","C:usr"},+{"\\\\\\","\\"},+{"\\\\\\\\","\\"},+#if defined(NO_LIBGEN_H)+{"C:","C:."},+#else++/* win32 platform variations: */+#if defined(__MINGW32__)+/* the following is clearly wrong ... */+{"C:","."},+#endif++#if defined(_MSC_VER)+{"C:","C:."},+#endif++#endif+#endif+{NULL,"."},+{NULL,NULL}+};+intmain(intargc,char**argv){if(argc==3&&!strcmp(argv[1],"normalize_path_copy")){
@@ -133,6 +295,12 @@ int main(int argc, char **argv)return0;}+if(argc==2&&!strcmp(argv[1],"basename"))+returntest_function(basename_data,basename,argv[1]);++if(argc==2&&!strcmp(argv[1],"dirname"))+returntest_function(dirname_data,dirname,argv[1]);+fprintf(stderr,"%s: unknown function name: %s\n",argv[0],argv[1]?argv[1]:"(there was none)");return1;
From: Johannes Schindelin <hidden> Date: 2016-06-15 23:07:40
According to POSIX, basename("/path/") should return "path", not
"path/". Likewise, basename(NULL) and basename("abc") should both
return ".".
Signed-off-by: Johannes Schindelin <redacted>
---
compat/basename.c | 20 +++++++++++++++++---
1 file changed, 17 insertions(+), 3 deletions(-)
From: Eric Sunshine <hidden> Date: 2016-06-15 23:07:40
On Fri, Jan 8, 2016 at 11:21 AM, Johannes Schindelin
[off-list ref] wrote:
Junio Hamano pointed out that there is an implicit assumption in pretty
much all the code calling has_dos_drive_prefix(): it assumes that the
DOS drive prefix is always two bytes long.
While this assumption is pretty safe, we can still make the code more
readable and less error-prone by introducing a function that skips the
DOS drive prefix safely.
While at it, we change the has_dos_drive_prefix() return value: it now
returns the number of bytes to be skipped if there is a DOS drive prefix.
With this change, code such as:
for (i = has_dos_drive_prefix(src); i > 0; i--)
...
in path.c reads a bit oddly. Renaming the function might help. For instance:
for (i = dos_drive_prefix_len(src); i > 0; i--)
...
From: Eric Sunshine <hidden> Date: 2016-06-15 23:07:40
On Fri, Jan 8, 2016 at 11:21 AM, Johannes Schindelin
[off-list ref] wrote:
quoted hunk
Unfortunately, some libgen implementations yield outcomes different from
what Git expects. For example, mingw-w64-crt provides a basename()
function, that shortens `path0/` to `path`!
So let's verify that the basename() and dirname() functions we use conform
to what Git expects.
Derived-from-code-by: Ramsay Jones [off-list ref]
Signed-off-by: Johannes Schindelin <redacted>
---
From: Johannes Schindelin <hidden> Date: 2016-06-15 23:07:41
Hi Eric,
On Sat, 9 Jan 2016, Eric Sunshine wrote:
On Fri, Jan 8, 2016 at 11:21 AM, Johannes Schindelin
[off-list ref] wrote:
quoted
Unfortunately, some libgen implementations yield outcomes different from
what Git expects. For example, mingw-w64-crt provides a basename()
function, that shortens `path0/` to `path`!
So let's verify that the basename() and dirname() functions we use conform
to what Git expects.
Derived-from-code-by: Ramsay Jones [off-list ref]
Signed-off-by: Johannes Schindelin <redacted>
---
From: Johannes Schindelin <hidden> Date: 2016-06-15 23:07:41
Junio Hamano pointed out that there is an implicit assumption in pretty
much all the code calling has_dos_drive_prefix(): it assumes that the
DOS drive prefix is always two bytes long.
While this assumption is pretty safe, we can still make the code more
readable and less error-prone by introducing a function that skips the
DOS drive prefix safely.
While at it, we change the has_dos_drive_prefix() return value: it now
returns the number of bytes to be skipped if there is a DOS drive prefix.
Signed-off-by: Johannes Schindelin <redacted>
---
compat/basename.c | 4 +---
compat/mingw.c | 14 +++++---------
compat/mingw.h | 10 +++++++++-
git-compat-util.h | 8 ++++++++
path.c | 14 +++++---------
5 files changed, 28 insertions(+), 22 deletions(-)
@@ -4,9 +4,7 @@char*gitbasename(char*path){constchar*base;-/* Skip over the disk name in MSDOS pathnames. */-if(has_dos_drive_prefix(path))-path+=2;+skip_dos_drive_prefix(&path);for(base=path;*path;path++){if(is_dir_sep(*path))base=path+1;
From: Johannes Schindelin <hidden> Date: 2016-06-15 23:07:41
According to POSIX, basename("/path/") should return "path", not
"path/". Likewise, basename(NULL) and basename("") should both
return "." to conform.
Signed-off-by: Johannes Schindelin <redacted>
---
compat/basename.c | 20 +++++++++++++++++---
1 file changed, 17 insertions(+), 3 deletions(-)
From: Johannes Schindelin <hidden> Date: 2016-06-15 23:07:41
Unfortunately, some libgen implementations yield outcomes different
from what Git expects. For example, mingw-w64-crt provides a basename()
function, that shortens `path0/` to `path`!
So let's verify that the basename() and dirname() functions we use
conform to what Git expects.
Derived-from-code-by: Ramsay Jones [off-list ref]
Signed-off-by: Johannes Schindelin <redacted>
---
t/t0060-path-utils.sh | 3 +
test-path-utils.c | 166 ++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 169 insertions(+)
@@ -39,6 +39,166 @@ static void normalize_argv_string(const char **var, const char *input)die("Bad value: %s\n",input);}+structtest_data{+constchar*from;/* input: transform from this ... */+constchar*to;/* output: ... to this. */+};++staticinttest_function(structtest_data*data,char*(*func)(char*input),+constchar*funcname)+{+intfailed=0,i;+charbuffer[1024];+char*to;++for(i=0;data[i].to;i++){+if(!data[i].from)+to=func(NULL);+else{+strcpy(buffer,data[i].from);+to=func(buffer);+}+if(strcmp(to,data[i].to)){+error("FAIL: %s(%s) => '%s' != '%s'\n",+funcname,data[i].from,to,data[i].to);+failed=1;+}+}+returnfailed;+}++staticstructtest_databasename_data[]={+/* --- POSIX type paths --- */+{NULL,"."},+{"","."},+{".","."},+{"..",".."},+{"/","/"},+#if defined(__CYGWIN__) && !defined(NO_LIBGEN_H)+{"//","//"},+{"///","//"},+{"////","//"},+#else+{"//","/"},+{"///","/"},+{"////","/"},+#endif+{"usr","usr"},+{"/usr","usr"},+{"/usr/","usr"},+{"/usr//","usr"},+{"/usr/lib","lib"},+{"usr/lib","lib"},+{"usr/lib///","lib"},++#if defined(__MINGW32__) || defined(_MSC_VER)++/* --- win32 type paths --- */+{"\\usr","usr"},+{"\\usr\\","usr"},+{"\\usr\\\\","usr"},+{"\\usr\\lib","lib"},+{"usr\\lib","lib"},+{"usr\\lib\\\\\\","lib"},+{"C:/usr","usr"},+{"C:/usr","usr"},+{"C:/usr/","usr"},+{"C:/usr//","usr"},+{"C:/usr/lib","lib"},+{"C:usr/lib","lib"},+{"C:usr/lib///","lib"},+{"C:","."},+{"C:a","a"},+{"C:/","/"},+{"C:///","/"},+#if defined(NO_LIBGEN_H)+{"\\","\\"},+{"\\\\","\\"},+{"\\\\\\","\\"},+#else++/* win32 platform variations: */+#if defined(__MINGW32__)+{"\\","/"},+{"\\\\","/"},+{"\\\\\\","/"},+#endif++#if defined(_MSC_VER)+{"\\","\\"},+{"\\\\","\\"},+{"\\\\\\","\\"},+#endif++#endif+#endif+{NULL,NULL}+};++staticstructtest_datadirname_data[]={+/* --- POSIX type paths --- */+{NULL,"."},+{"","."},+{".","."},+{"..","."},+{"/","/"},+{"//","//"},+#if defined(__CYGWIN__) && !defined(NO_LIBGEN_H)+{"///","//"},+{"////","//"},+#else+{"///","/"},+{"////","/"},+#endif+{"usr","."},+{"/usr","/"},+{"/usr/","/"},+{"/usr//","/"},+{"/usr/lib","/usr"},+{"usr/lib","usr"},+{"usr/lib///","usr"},++#if defined(__MINGW32__) || defined(_MSC_VER)++/* --- win32 type paths --- */+{"\\","\\"},+{"\\\\","\\\\"},+{"\\usr","\\"},+{"\\usr\\","\\"},+{"\\usr\\\\","\\"},+{"\\usr\\lib","\\usr"},+{"usr\\lib","usr"},+{"usr\\lib\\\\\\","usr"},+{"C:a","C:."},+{"C:/","C:/"},+{"C:///","C:/"},+{"C:/usr","C:/"},+{"C:/usr/","C:/"},+{"C:/usr//","C:/"},+{"C:/usr/lib","C:/usr"},+{"C:usr/lib","C:usr"},+{"C:usr/lib///","C:usr"},+{"\\\\\\","\\"},+{"\\\\\\\\","\\"},+#if defined(NO_LIBGEN_H)+{"C:","C:."},+#else++/* win32 platform variations: */+#if defined(__MINGW32__)+/* the following is clearly wrong ... */+{"C:","."},+#endif++#if defined(_MSC_VER)+{"C:","C:."},+#endif++#endif+#endif+{NULL,NULL}+};+intmain(intargc,char**argv){if(argc==3&&!strcmp(argv[1],"normalize_path_copy")){
@@ -133,6 +293,12 @@ int main(int argc, char **argv)return0;}+if(argc==2&&!strcmp(argv[1],"basename"))+returntest_function(basename_data,basename,argv[1]);++if(argc==2&&!strcmp(argv[1],"dirname"))+returntest_function(dirname_data,dirname,argv[1]);+fprintf(stderr,"%s: unknown function name: %s\n",argv[0],argv[1]?argv[1]:"(there was none)");return1;
From: Johannes Schindelin <hidden> Date: 2016-06-15 23:07:41
When there is no `libgen.h` to our disposal, we miss the `dirname()`
function.
So far, we only had one user of that function: credential-cache--daemon
(which was only compiled when Unix sockets are available, anyway). But
now we also have `builtin/am.c` as user, so we need it.
Since `dirname()` is a sibling of `basename()`, we simply put our very
own `gitdirname()` implementation next to `gitbasename()` and use it
if `NO_LIBGEN_H` has been set.
Signed-off-by: Johannes Schindelin <redacted>
---
compat/basename.c | 44 ++++++++++++++++++++++++++++++++++++++++++++
git-compat-util.h | 2 ++
2 files changed, 46 insertions(+)
From: Eric Sunshine <hidden> Date: 2016-06-15 23:07:41
On Mon, Jan 11, 2016 at 1:30 PM, Johannes Schindelin
[off-list ref] wrote:
quoted hunk
When there is no `libgen.h` to our disposal, we miss the `dirname()`
function.
So far, we only had one user of that function: credential-cache--daemon
(which was only compiled when Unix sockets are available, anyway). But
now we also have `builtin/am.c` as user, so we need it.
Since `dirname()` is a sibling of `basename()`, we simply put our very
own `gitdirname()` implementation next to `gitbasename()` and use it
if `NO_LIBGEN_H` has been set.
Signed-off-by: Johannes Schindelin <redacted>
---
@@ -25,3 +26,46 @@ char *gitbasename (char *path)+char *gitdirname(char *path)+{+ char *p = path, *slash = NULL, c;+ int dos_drive_prefix;++ if (!p)+ return ".";++ if ((dos_drive_prefix = skip_dos_drive_prefix(&p)) && !*p) {+ static struct strbuf buf = STRBUF_INIT;++dot:+ strbuf_reset(&buf);+ strbuf_addf(&buf, "%.*s.", dos_drive_prefix, path);+ return buf.buf;+ }++ /*+ * POSIX.1-2001 says dirname("/") should return "/", and dirname("//")+ * should return "//", but dirname("///") should return "/" again.+ */+ if (is_dir_sep(*p)) {+ if (!p[1] || (is_dir_sep(p[1]) && !p[2]))+ return path;+ slash = ++p;+ }+ while ((c = *(p++)))+ if (is_dir_sep(c)) {+ char *tentative = p - 1;++ /* POSIX.1-2001 says to ignore trailing slashes */+ while (is_dir_sep(*p))+ p++;+ if (*p)+ slash = tentative;+ }++ if (!slash)+ goto dot;+ *slash = '\0';+ return path;+}
I wonder if this would be a bit easier to follow if it was structured
something like this:
static struct strbuf buf = STRBUF_INIT;
if ((dos_drive_prefix = skip_dos_drive_prefix(&p)) && !*p)
goto dot;
...
if (is_dir_sep(*p)) {
...
}
...
while ((c = *(p++)))
...
if (slash) {
*slash = '\0';
return path;
}
dot:
strbuf_reset(&buf);
strbuf_addf(&buf, "%.*s.", dos_drive_prefix, path);
return buf.buf;
From: Johannes Schindelin <hidden> Date: 2016-06-15 23:07:42
Junio Hamano pointed out that there is an implicit assumption in pretty
much all the code calling has_dos_drive_prefix(): it assumes that the
DOS drive prefix is always two bytes long.
While this assumption is pretty safe, we can still make the code more
readable and less error-prone by introducing a function that skips the
DOS drive prefix safely.
While at it, we change the has_dos_drive_prefix() return value: it now
returns the number of bytes to be skipped if there is a DOS drive prefix.
Signed-off-by: Johannes Schindelin <redacted>
---
compat/basename.c | 4 +---
compat/mingw.c | 14 +++++---------
compat/mingw.h | 10 +++++++++-
git-compat-util.h | 8 ++++++++
path.c | 14 +++++---------
5 files changed, 28 insertions(+), 22 deletions(-)
@@ -4,9 +4,7 @@char*gitbasename(char*path){constchar*base;-/* Skip over the disk name in MSDOS pathnames. */-if(has_dos_drive_prefix(path))-path+=2;+skip_dos_drive_prefix(&path);for(base=path;*path;path++){if(is_dir_sep(*path))base=path+1;
From: Johannes Schindelin <hidden> Date: 2016-06-15 23:07:42
According to POSIX, basename("/path/") should return "path", not
"path/". Likewise, basename(NULL) and basename("") should both
return "." to conform.
Signed-off-by: Johannes Schindelin <redacted>
---
compat/basename.c | 20 +++++++++++++++++---
1 file changed, 17 insertions(+), 3 deletions(-)
From: Johannes Schindelin <hidden> Date: 2016-06-15 23:07:42
When there is no `libgen.h` to our disposal, we miss the `dirname()`
function.
So far, we only had one user of that function: credential-cache--daemon
(which was only compiled when Unix sockets are available, anyway). But
now we also have `builtin/am.c` as user, so we need it.
Since `dirname()` is a sibling of `basename()`, we simply put our very
own `gitdirname()` implementation next to `gitbasename()` and use it
if `NO_LIBGEN_H` has been set.
Signed-off-by: Johannes Schindelin <redacted>
---
compat/basename.c | 44 ++++++++++++++++++++++++++++++++++++++++++++
git-compat-util.h | 2 ++
2 files changed, 46 insertions(+)
From: Johannes Schindelin <hidden> Date: 2016-06-15 23:07:42
Unfortunately, some libgen implementations yield outcomes different
from what Git expects. For example, mingw-w64-crt provides a basename()
function, that shortens `path0/` to `path`!
So let's verify that the basename() and dirname() functions we use
conform to what Git expects.
Derived-from-code-by: Ramsay Jones [off-list ref]
Signed-off-by: Johannes Schindelin <redacted>
---
t/t0060-path-utils.sh | 3 +
test-path-utils.c | 166 ++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 169 insertions(+)
@@ -39,6 +39,166 @@ static void normalize_argv_string(const char **var, const char *input)die("Bad value: %s\n",input);}+structtest_data{+constchar*from;/* input: transform from this ... */+constchar*to;/* output: ... to this. */+};++staticinttest_function(structtest_data*data,char*(*func)(char*input),+constchar*funcname)+{+intfailed=0,i;+charbuffer[1024];+char*to;++for(i=0;data[i].to;i++){+if(!data[i].from)+to=func(NULL);+else{+strcpy(buffer,data[i].from);+to=func(buffer);+}+if(strcmp(to,data[i].to)){+error("FAIL: %s(%s) => '%s' != '%s'\n",+funcname,data[i].from,to,data[i].to);+failed=1;+}+}+returnfailed;+}++staticstructtest_databasename_data[]={+/* --- POSIX type paths --- */+{NULL,"."},+{"","."},+{".","."},+{"..",".."},+{"/","/"},+#if defined(__CYGWIN__) && !defined(NO_LIBGEN_H)+{"//","//"},+{"///","//"},+{"////","//"},+#else+{"//","/"},+{"///","/"},+{"////","/"},+#endif+{"usr","usr"},+{"/usr","usr"},+{"/usr/","usr"},+{"/usr//","usr"},+{"/usr/lib","lib"},+{"usr/lib","lib"},+{"usr/lib///","lib"},++#if defined(__MINGW32__) || defined(_MSC_VER)++/* --- win32 type paths --- */+{"\\usr","usr"},+{"\\usr\\","usr"},+{"\\usr\\\\","usr"},+{"\\usr\\lib","lib"},+{"usr\\lib","lib"},+{"usr\\lib\\\\\\","lib"},+{"C:/usr","usr"},+{"C:/usr","usr"},+{"C:/usr/","usr"},+{"C:/usr//","usr"},+{"C:/usr/lib","lib"},+{"C:usr/lib","lib"},+{"C:usr/lib///","lib"},+{"C:","."},+{"C:a","a"},+{"C:/","/"},+{"C:///","/"},+#if defined(NO_LIBGEN_H)+{"\\","\\"},+{"\\\\","\\"},+{"\\\\\\","\\"},+#else++/* win32 platform variations: */+#if defined(__MINGW32__)+{"\\","/"},+{"\\\\","/"},+{"\\\\\\","/"},+#endif++#if defined(_MSC_VER)+{"\\","\\"},+{"\\\\","\\"},+{"\\\\\\","\\"},+#endif++#endif+#endif+{NULL,NULL}+};++staticstructtest_datadirname_data[]={+/* --- POSIX type paths --- */+{NULL,"."},+{"","."},+{".","."},+{"..","."},+{"/","/"},+{"//","//"},+#if defined(__CYGWIN__) && !defined(NO_LIBGEN_H)+{"///","//"},+{"////","//"},+#else+{"///","/"},+{"////","/"},+#endif+{"usr","."},+{"/usr","/"},+{"/usr/","/"},+{"/usr//","/"},+{"/usr/lib","/usr"},+{"usr/lib","usr"},+{"usr/lib///","usr"},++#if defined(__MINGW32__) || defined(_MSC_VER)++/* --- win32 type paths --- */+{"\\","\\"},+{"\\\\","\\\\"},+{"\\usr","\\"},+{"\\usr\\","\\"},+{"\\usr\\\\","\\"},+{"\\usr\\lib","\\usr"},+{"usr\\lib","usr"},+{"usr\\lib\\\\\\","usr"},+{"C:a","C:."},+{"C:/","C:/"},+{"C:///","C:/"},+{"C:/usr","C:/"},+{"C:/usr/","C:/"},+{"C:/usr//","C:/"},+{"C:/usr/lib","C:/usr"},+{"C:usr/lib","C:usr"},+{"C:usr/lib///","C:usr"},+{"\\\\\\","\\"},+{"\\\\\\\\","\\"},+#if defined(NO_LIBGEN_H)+{"C:","C:."},+#else++/* win32 platform variations: */+#if defined(__MINGW32__)+/* the following is clearly wrong ... */+{"C:","."},+#endif++#if defined(_MSC_VER)+{"C:","C:."},+#endif++#endif+#endif+{NULL,NULL}+};+intmain(intargc,char**argv){if(argc==3&&!strcmp(argv[1],"normalize_path_copy")){
@@ -133,6 +293,12 @@ int main(int argc, char **argv)return0;}+if(argc==2&&!strcmp(argv[1],"basename"))+returntest_function(basename_data,basename,argv[1]);++if(argc==2&&!strcmp(argv[1],"dirname"))+returntest_function(dirname_data,dirname,argv[1]);+fprintf(stderr,"%s: unknown function name: %s\n",argv[0],argv[1]?argv[1]:"(there was none)");return1;
From: Ramsay Jones <hidden> Date: 2016-06-15 23:07:43
Hi Johannes,
Sorry for not commenting sooner, I've been away from email for
a few days. Also, I have only just looked at what is currently
in pu (@1a05310), which I'm pretty sure is v3 of this series.
On 12/01/16 07:57, Johannes Schindelin wrote:
This mini series adds a fall-back for the `dirname()` function that we use
e.g. in git-am. This is necessary because not all platforms have a working
libgen.h.
While at it, we ensure that our basename() drop-in conforms to the POSIX
specifications.
I was somewhat disappointed that you ignored the implementation of
gitbasename() and gitdirname() that was included in the test-libgen.c
file that I sent you. I had hoped they would be (at worst) a good starting
point if you found them to be lacking for your use case (ie. for the
64-bit versions of MSVC/MinGW).
Did you have any test cases that failed? (If so, could you please add
them to the tests).
Hmm, I just had another look at them and recalled one of my TODO items.
Ahem, yes, ... err, replace code which provoked undefined behaviour. :-P
Actually, that took just ten minutes to fix. (patch below)
In addition to Eric's style improvement, v4 also fixes the signature
of skip_dos_drive_prefix() in the non-Windows case.
Yes, this fixes one of my comments about v3.
ATB,
Ramsay Jones
-- >8 --
From: Ramsay Jones <redacted>
Date: Tue, 12 Jan 2016 23:28:09 +0000
Subject: [PATCH] test-libgen.c: don't provoke undefined behaviour
---
test-libgen.c | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
From: Ramsay Jones <hidden> Date: 2016-06-15 23:07:43
On 12/01/16 07:57, Johannes Schindelin wrote:
quoted hunk
According to POSIX, basename("/path/") should return "path", not
"path/". Likewise, basename(NULL) and basename("") should both
return "." to conform.
Signed-off-by: Johannes Schindelin <redacted>
---
compat/basename.c | 20 +++++++++++++++++---
1 file changed, 17 insertions(+), 3 deletions(-)
I don't suppose it makes much difference, but I find my version
slightly easier to read:
char *gitbasename (char *path)
{
char *p;
if (!path || !*path)
return ".";
/* skip drive designator, if any */
if (has_dos_drive_prefix(path))
path += 2;
if (!*path)
return ".";
/* trim trailing directory separators */
p = path + strlen(path) - 1;
while (is_dir_sep(*p)) {
if (p == path)
return path;
*p-- = '\0';
}
/* find begining of last path component */
while (p > path && !is_dir_sep(*p))
p--;
if (is_dir_sep(*p))
p++;
return p;
}
ATB,
Ramsay Jones
From: Ramsay Jones <hidden> Date: 2016-06-15 23:07:43
On 12/01/16 07:57, Johannes Schindelin wrote:
quoted hunk
When there is no `libgen.h` to our disposal, we miss the `dirname()`
function.
So far, we only had one user of that function: credential-cache--daemon
(which was only compiled when Unix sockets are available, anyway). But
now we also have `builtin/am.c` as user, so we need it.
Since `dirname()` is a sibling of `basename()`, we simply put our very
own `gitdirname()` implementation next to `gitbasename()` and use it
if `NO_LIBGEN_H` has been set.
Signed-off-by: Johannes Schindelin <redacted>
---
compat/basename.c | 44 ++++++++++++++++++++++++++++++++++++++++++++
git-compat-util.h | 2 ++
2 files changed, 46 insertions(+)
Also, when compiling on Cygwin with NO_LIBGEN_H, I need to
include the following here:
#undef basename
in order to suppress approx 230 warnings about the redefinition
of the basename macro.
(I suppose that should go in the previous commit. dunno)
From: Ramsay Jones <hidden> Date: 2016-06-15 23:07:43
On 12/01/16 07:57, Johannes Schindelin wrote:
quoted hunk
Unfortunately, some libgen implementations yield outcomes different
from what Git expects. For example, mingw-w64-crt provides a basename()
function, that shortens `path0/` to `path`!
So let's verify that the basename() and dirname() functions we use
conform to what Git expects.
Derived-from-code-by: Ramsay Jones [off-list ref]
Signed-off-by: Johannes Schindelin <redacted>
---
t/t0060-path-utils.sh | 3 +
test-path-utils.c | 166 ++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 169 insertions(+)
From: Johannes Schindelin <hidden> Date: 2016-06-15 23:07:43
Hi Ramsay,
On Wed, 13 Jan 2016, Ramsay Jones wrote:
On 12/01/16 07:57, Johannes Schindelin wrote:
quoted
This mini series adds a fall-back for the `dirname()` function that we use
e.g. in git-am. This is necessary because not all platforms have a working
libgen.h.
While at it, we ensure that our basename() drop-in conforms to the POSIX
specifications.
I was somewhat disappointed that you ignored the implementation of
gitbasename() and gitdirname() that was included in the test-libgen.c
file that I sent you.
I am sorry you feel that I ignored your work!
My line of reasoning, however, was to go with the existing gitbasename()
and with the gitdirname() I had come up with, because I was already
familiar with them.
Your tests included a couple of corner cases that neither handled
correctly, and I was able to fix that, so I was happy.
To be quite honest, I blindly deleted everything but the tests, noticed
that the remaining code looked eerily similar to test-path-utils, and
merged it there.
Ciao,
Dscho
I don't suppose it makes much difference, but I find my version
slightly easier to read:
Yours is better documented, yes, but as I said, I started from what Git
already had and tried to provide as minimal changes as possible, to make
reviewing easy. In any case, I am very reluctant when it comes to
wholesale code replacements: in my experience, these frequently lead to
new, entertaining and unintended behavior. I worked with somebody who (for
the sake of charity) in the following I will reference only by his most
frequent commit message: Dr "Completely new version" (and yes, this was
the extent of the commit message). If you buy me a beer or three, I will
gladly tell you all the fun I had trying to find the regressions in that
code.
In short: please accept that my decision to build on the existing code
rather than replacing it had nothing to do with your code.
Ciao,
Dscho
From: Johannes Schindelin <hidden> Date: 2016-06-15 23:07:43
Hi Ramsay,
On Wed, 13 Jan 2016, Ramsay Jones wrote:
Also, when compiling on Cygwin with NO_LIBGEN_H, I need to
include the following here:
#undef basename
in order to suppress approx 230 warnings about the redefinition
of the basename macro.
(I suppose that should go in the previous commit. dunno)
I think this is an incorrect use of NO_LIBGEN_H (because Cygwin obviously
has it), but in any case, it is a completely independent issue from
fixing/testing basename()/dirname(), so your #undef basename should be in
a completely separate commit, methinks.
Ciao,
Dscho
From: Ramsay Jones <hidden> Date: 2016-06-15 23:07:44
On 13/01/16 07:40, Johannes Schindelin wrote:
Hi Ramsay,
On Wed, 13 Jan 2016, Ramsay Jones wrote:
quoted
Also, when compiling on Cygwin with NO_LIBGEN_H, I need to
include the following here:
#undef basename
in order to suppress approx 230 warnings about the redefinition
of the basename macro.
(I suppose that should go in the previous commit. dunno)
I think this is an incorrect use of NO_LIBGEN_H (because Cygwin obviously
has it), but in any case, it is a completely independent issue from
fixing/testing basename()/dirname(), so your #undef basename should be in
a completely separate commit, methinks.
OK. I think this worked fine on 32-bit cygwin, but the system headers
have changed quite a bit on 64-bit cygwin and I only tried it for the
first time yesterday. (It was helpful in the debugging process at one
point to be able to build with NO_LIBGEN_H on all platforms ...)
ATB,
Ramsay Jones
From: Michael Blume <hidden> Date: 2016-06-15 23:07:44
On Fri, Jan 8, 2016 at 8:21 AM, Johannes Schindelin
[off-list ref] wrote:
quoted hunk
Unfortunately, some libgen implementations yield outcomes different from
what Git expects. For example, mingw-w64-crt provides a basename()
function, that shortens `path0/` to `path`!
So let's verify that the basename() and dirname() functions we use conform
to what Git expects.
Derived-from-code-by: Ramsay Jones [off-list ref]
Signed-off-by: Johannes Schindelin <redacted>
---
t/t0060-path-utils.sh | 3 +
test-path-utils.c | 168 ++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 171 insertions(+)
@@ -39,6 +39,168 @@ static void normalize_argv_string(const char **var, const char *input)die("Bad value: %s\n",input);}+structtest_data{+char*from;/* input: transform from this ... */+char*to;/* output: ... to this. */+};++staticinttest_function(structtest_data*data,char*(*func)(char*input),+constchar*funcname)+{+intfailed=0,i;+staticcharbuffer[1024];+char*to;++for(i=0;data[i].to;i++){+if(!data[i].from)+to=func(NULL);+else{+strcpy(buffer,data[i].from);+to=func(buffer);+}+if(strcmp(to,data[i].to)){+error("FAIL: %s(%s) => '%s' != '%s'\n",+funcname,data[i].from,to,data[i].to);+failed++;+}+}+return!!failed;+}++staticstructtest_databasename_data[]={+/* --- POSIX type paths --- */+{NULL,"."},+{"","."},+{".","."},+{"..",".."},+{"/","/"},+#if defined(__CYGWIN__) && !defined(NO_LIBGEN_H)+{"//","//"},+{"///","//"},+{"////","//"},+#else+{"//","/"},+{"///","/"},+{"////","/"},+#endif+{"usr","usr"},+{"/usr","usr"},+{"/usr/","usr"},+{"/usr//","usr"},+{"/usr/lib","lib"},+{"usr/lib","lib"},+{"usr/lib///","lib"},++#if defined(__MINGW32__) || defined(_MSC_VER)++/* --- win32 type paths --- */+{"\\usr","usr"},+{"\\usr\\","usr"},+{"\\usr\\\\","usr"},+{"\\usr\\lib","lib"},+{"usr\\lib","lib"},+{"usr\\lib\\\\\\","lib"},+{"C:/usr","usr"},+{"C:/usr","usr"},+{"C:/usr/","usr"},+{"C:/usr//","usr"},+{"C:/usr/lib","lib"},+{"C:usr/lib","lib"},+{"C:usr/lib///","lib"},+{"C:","."},+{"C:a","a"},+{"C:/","/"},+{"C:///","/"},+#if defined(NO_LIBGEN_H)+{"\\","\\"},+{"\\\\","\\"},+{"\\\\\\","\\"},+#else++/* win32 platform variations: */+#if defined(__MINGW32__)+{"\\","/"},+{"\\\\","/"},+{"\\\\\\","/"},+#endif++#if defined(_MSC_VER)+{"\\","\\"},+{"\\\\","\\"},+{"\\\\\\","\\"},+#endif++#endif+#endif+{NULL,"."},+{NULL,NULL}+};++staticstructtest_datadirname_data[]={+/* --- POSIX type paths --- */+{NULL,"."},+{"","."},+{".","."},+{"..","."},+{"/","/"},+{"//","//"},+#if defined(__CYGWIN__) && !defined(NO_LIBGEN_H)+{"///","//"},+{"////","//"},+#else+{"///","/"},+{"////","/"},+#endif+{"usr","."},+{"/usr","/"},+{"/usr/","/"},+{"/usr//","/"},+{"/usr/lib","/usr"},+{"usr/lib","usr"},+{"usr/lib///","usr"},++#if defined(__MINGW32__) || defined(_MSC_VER)++/* --- win32 type paths --- */+{"\\","\\"},+{"\\\\","\\\\"},+{"\\usr","\\"},+{"\\usr\\","\\"},+{"\\usr\\\\","\\"},+{"\\usr\\lib","\\usr"},+{"usr\\lib","usr"},+{"usr\\lib\\\\\\","usr"},+{"C:a","C:."},+{"C:/","C:/"},+{"C:///","C:/"},+{"C:/usr","C:/"},+{"C:/usr/","C:/"},+{"C:/usr//","C:/"},+{"C:/usr/lib","C:/usr"},+{"C:usr/lib","C:usr"},+{"C:usr/lib///","C:usr"},+{"\\\\\\","\\"},+{"\\\\\\\\","\\"},+#if defined(NO_LIBGEN_H)+{"C:","C:."},+#else++/* win32 platform variations: */+#if defined(__MINGW32__)+/* the following is clearly wrong ... */+{"C:","."},+#endif++#if defined(_MSC_VER)+{"C:","C:."},+#endif++#endif+#endif+{NULL,"."},+{NULL,NULL}+};+intmain(intargc,char**argv){if(argc==3&&!strcmp(argv[1],"normalize_path_copy")){
@@ -133,6 +295,12 @@ int main(int argc, char **argv)return0;}+if(argc==2&&!strcmp(argv[1],"basename"))+returntest_function(basename_data,basename,argv[1]);++if(argc==2&&!strcmp(argv[1],"dirname"))+returntest_function(dirname_data,dirname,argv[1]);+fprintf(stderr,"%s: unknown function name: %s\n",argv[0],argv[1]?argv[1]:"(there was none)");return1;--
2.6.3.windows.1.300.g1c25e49
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Test fails on my Mac:
expecting success: test-path-utils dirname
error: FAIL: dirname(//) => '/' != '//'
not ok 2 - dirname
# test-path-utils dirname
This triggers
CC alloc.o
In file included from git-compat-util.h:186,
from cache.h:4,
from alloc.c:12:
compat/mingw.h: In function 'mingw_skip_dos_drive_prefix':
compat/mingw.h:365: warning: implicit declaration of function 'isalpha'
when I build under the old MSYS environment. While I would understand
that the old MSYS environment is end-of-lifed and not worth your time
catering to, the error is still an indication of a problem.
Notice that mingw.h is #included in line 186 of git-compat-util.h,
isalpha is only (re-)defined much later in line 790. That would explain
the warning. What I do not understand is that you do not observe the
same warning in your MSYS2/MINGWxx environment. It would mean that
<ctype.h> is included somewhere.
At any rate, the resulting binary sometimes uses an isalpha
implementation other than the one provided in git-compat-util.h. The
result is most likely correct, but it is certainly not the intent,
is it?
I did not attempt to build with MSVC, but it is not unlikely that it
shows the same error.
I suggest to move the function definition out of line: