All of lore.kernel.org
 help / color / mirror / Atom feed
From: Ramsay Jones <ramsay@ramsayjones.plus.com>
To: Johannes Schindelin <johannes.schindelin@gmx.de>,
	Junio C Hamano <gitster@pobox.com>
Cc: git@vger.kernel.org
Subject: Re: [PATCH] Provide a dirname() function when NO_LIBGEN_H=YesPlease
Date: Wed, 30 Sep 2015 19:57:53 +0100	[thread overview]
Message-ID: <560C30B1.3010508@ramsayjones.plus.com> (raw)
In-Reply-To: <25a2598e756959f55f06ae6b4dc6f448e3b6b127.1443624188.git.johannes.schindelin@gmx.de>

[-- Attachment #1: Type: text/plain, Size: 3186 bytes --]

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


> 
> 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 <johannes.schindelin@gmx.de>
> ---
> 
> 	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(+)
> 
> diff --git a/compat/basename.c b/compat/basename.c
> index d8f8a3c..10dba38 100644
> --- a/compat/basename.c
> +++ b/compat/basename.c
> @@ -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';
> +	return path;
> +}
> diff --git a/git-compat-util.h b/git-compat-util.h
> index f649e81..8b01aa5 100644
> --- a/git-compat-util.h
> +++ b/git-compat-util.h
> @@ -253,6 +253,8 @@ struct itimerval {
>  #else
>  #define basename gitbasename
>  extern char *gitbasename(char *);
> +#define dirname gitdirname
> +extern char *gitdirname(char *);
>  #endif
>  
>  #ifndef NO_ICONV
> 

[-- Attachment #2: test-libgen.c --]
[-- Type: text/x-csrc, Size: 6506 bytes --]

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#ifndef NO_LIBGEN_H
# include <libgen.h>
#endif

struct test_data {
	char *from;  /* input:  transform from this ... */
	char *to;    /* output: ... to this.            */
};

#ifdef NO_LIBGEN_H

#if defined(__MINGW32__) || defined(_MSC_VER)
#define has_dos_drive_prefix(path) (isalpha(*(path)) && (path)[1] == ':')
#define is_dir_sep(c) ((c) == '/' || (c) == '\\')
#else
#define has_dos_drive_prefix(path) 0
#define is_dir_sep(c) ((c) == '/')
#endif

#define basename gitbasename
#define dirname gitdirname

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--;
	return p + 1;
}

char *gitdirname(char *path)
{
	char *p, *start;

	if (!path || !*path)
		return ".";
	start = path;
	/* skip drive designator, if any */
	if (has_dos_drive_prefix(path))
		start += 2;
	/* check for // */
	if (strcmp(start, "//") == 0)
		return path;
	/* check for \\ */
	if (is_dir_sep('\\') && strcmp(start, "\\\\") == 0)
		return path;
	/* trim trailing directory separators */
	p = path + strlen(path) - 1;
	while (is_dir_sep(*p)) {
		if (p == start)
			return path;
		*p-- = '\0';
	}
	/* find begining of last path component */
	while (p >= start && !is_dir_sep(*p))
		p--;
	/* terminate dirname */
	if (p < start) {
		p = start;
		*p++ = '.';
	} else if (p == start)
		p++;
	*p = '\0';
	return path;
}

#endif

static int test_basename(void)
{
	static struct test_data t[] = {

		/* --- 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,              "."    }
	};
	static char input[1024];
	char *from, *to;
	int i, failed = 0;

	for (i = 0; i < sizeof(t)/sizeof(t[0]); i++) {
		from = NULL;
		if (t[i].from) {
			strcpy(input, t[i].from);
			from = input;
		}
		to = basename(from);
		if (strcmp(to, t[i].to) != 0) {
			fprintf(stderr, "FAIL: basename(%s) => '%s' != '%s'\n",
				t[i].from, to, t[i].to);
			failed++;
		}
	}
	return failed != 0;
}

static int test_dirname(void)
{
	static struct test_data t[] = {

		/* --- 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,              "."      }
	};
	static char input[1024];
	char *from, *to;
	int i, failed = 0;

	for (i = 0; i < sizeof(t)/sizeof(t[0]); i++) {
		from = NULL;
		if (t[i].from) {
			strcpy(input, t[i].from);
			from = input;
		}
		to = dirname(from);
		if (strcmp(to, t[i].to) != 0) {
			fprintf(stderr, "FAIL: dirname(%s) => '%s' != '%s'\n",
				t[i].from, to, t[i].to);
			failed++;
		}
	}
	return failed != 0;
}

int main(int argc, char **argv)
{
	if (argc == 2 && !strcmp(argv[1], "basename"))
		return test_basename();

	if (argc == 2 && !strcmp(argv[1], "dirname"))
		return test_dirname();

	fprintf(stderr, "%s: unknown function name: %s\n", argv[0],
		argv[1] ? argv[1] : "(there was none)");
	return 1;
}

  parent reply	other threads:[~2015-09-30 18:58 UTC|newest]

Thread overview: 63+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-09-30 14:50 [PATCH] Provide a dirname() function when NO_LIBGEN_H=YesPlease Johannes Schindelin
2015-09-30 18:24 ` Junio C Hamano
2016-01-08 16:17   ` Johannes Schindelin
2015-09-30 18:57 ` Ramsay Jones [this message]
2016-01-08 16:18   ` Johannes Schindelin
2016-01-08 16:21 ` [PATCH v2 0/4] Ensure that we can build without libgen.h Johannes Schindelin
2016-01-08 16:21   ` [PATCH v2 1/4] Refactor skipping DOS drive prefixes Johannes Schindelin
2016-01-08 21:51     ` Eric Sunshine
2016-01-08 22:07       ` Junio C Hamano
2016-01-11  9:32         ` Johannes Schindelin
2016-01-11 15:58           ` Junio C Hamano
2016-01-08 16:21   ` [PATCH v2 2/4] compat/basename: make basename() conform to POSIX Johannes Schindelin
2016-01-08 18:45     ` Junio C Hamano
2016-01-09 14:53       ` Johannes Schindelin
2016-01-11 16:01         ` Junio C Hamano
2016-01-08 16:21   ` [PATCH v2 3/4] Provide a dirname() function when NO_LIBGEN_H=YesPlease Johannes Schindelin
2016-01-08 18:53     ` Junio C Hamano
2016-01-08 16:21   ` [PATCH v2 4/4] t0060: verify that basename() and dirname() work as expected Johannes Schindelin
2016-01-10  4:17     ` Eric Sunshine
2016-01-11 10:50       ` Johannes Schindelin
2016-01-13 18:52     ` Michael Blume
2016-01-13 19:43       ` Junio C Hamano
2016-01-14  6:09         ` Johannes Schindelin
2016-01-11 18:29   ` [PATCH v3 0/4] Ensure that we can build without libgen.h Johannes Schindelin
2016-01-11 18:29     ` [PATCH v3 1/4] Refactor skipping DOS drive prefixes Johannes Schindelin
2016-01-11 18:29     ` [PATCH v3 2/4] compat/basename: make basename() conform to POSIX Johannes Schindelin
2016-01-11 18:30     ` [PATCH v3 3/4] Provide a dirname() function when NO_LIBGEN_H=YesPlease Johannes Schindelin
2016-01-11 20:33       ` Eric Sunshine
2016-01-11 22:56         ` Junio C Hamano
2016-01-12  7:57           ` Johannes Schindelin
2016-01-11 18:30     ` [PATCH v3 4/4] t0060: verify that basename() and dirname() work as expected Johannes Schindelin
2016-01-11 22:59     ` [PATCH v3 0/4] Ensure that we can build without libgen.h Junio C Hamano
2016-01-12  7:57     ` [PATCH v4 " Johannes Schindelin
2016-01-12  7:57       ` [PATCH v4 1/4] Refactor skipping DOS drive prefixes Johannes Schindelin
2016-01-22 18:50         ` Johannes Sixt
2016-01-22 19:09           ` Junio C Hamano
2016-01-23  8:25             ` Johannes Schindelin
2016-01-23 19:02               ` Johannes Sixt
2016-01-24 10:56                 ` Johannes Schindelin
2016-01-24 12:36                   ` Johannes Sixt
2016-01-24 22:12                     ` Junio C Hamano
2016-01-25 21:47                       ` [PATCH] mingw: avoid linking to the C library's isalpha() Johannes Sixt
2016-01-25 22:04                         ` Junio C Hamano
2016-01-12  7:57       ` [PATCH v4 2/4] compat/basename: make basename() conform to POSIX Johannes Schindelin
2016-01-13  0:49         ` Ramsay Jones
2016-01-13  7:14           ` Johannes Schindelin
2016-01-12  7:57       ` [PATCH v4 3/4] Provide a dirname() function when NO_LIBGEN_H=YesPlease Johannes Schindelin
2016-01-13  0:55         ` Ramsay Jones
2016-01-13  7:40           ` Johannes Schindelin
2016-01-13 16:44             ` Ramsay Jones
2016-01-12  7:57       ` [PATCH v4 4/4] t0060: verify that basename() and dirname() work as expected Johannes Schindelin
2016-01-13  0:58         ` Ramsay Jones
2016-01-13  7:17           ` Johannes Schindelin
     [not found]         ` <5695E4FB.2060705@web.de>
2016-01-13  9:27           ` Johannes Schindelin
2016-01-13 16:46             ` Ramsay Jones
2016-01-13 16:34           ` Ramsay Jones
2016-01-13 17:20             ` Junio C Hamano
2016-01-13 18:53               ` Johannes Schindelin
2016-01-13  0:44       ` [PATCH v4 0/4] Ensure that we can build without libgen.h Ramsay Jones
2016-01-13  2:56         ` Junio C Hamano
2016-01-13  6:15           ` Torsten Bögershausen
2016-01-13  7:38             ` Johannes Schindelin
2016-01-13  7:02         ` Johannes Schindelin

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=560C30B1.3010508@ramsayjones.plus.com \
    --to=ramsay@ramsayjones.plus.com \
    --cc=git@vger.kernel.org \
    --cc=gitster@pobox.com \
    --cc=johannes.schindelin@gmx.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.