[patch] basename vs. __gnu_basename fix
Craig Howland
howland@LGSInnovations.com
Wed Apr 22 02:20:00 GMT 2015
On 04/21/2015 11:50 AM, Corinna Vinschen wrote:
> Hi,
>
> I'd like to propose the following patch. While running some autoconf
> script under a test version of Cygwin, the following problem turned up:
>
> In file included from conftest.c:429:0:
> /usr/include/libgen.h:18:14: error: conflicting types for 'basename'
> extern char *basename (char *path);
> ^
> In file included from /usr/include/stdio.h:29:0,
> from conftest.c:396:
> /usr/include/string.h:172:7: note: previous declaration of 'basename' was here
> char *_EXFUN(basename,(const char *))
> ^
>
> It turns out that the current method to define basename in libgen.h
> (POSIX) as well as in string.h (GNU) is prone to an ordering problem. I
> checked that this problem doesn't exist with glibc, and that the glibc
> method prefers the POSIX version over the GNU version if libgen.h is
> included, independently of the order of inclusion.
>
> However, this is a bit simpler in glibc because the symbol "basename"
> is the GNU version and "__xpg_basename" is the POSIX version and
> __xpg_basename is preferred by simply having
>
> #define basename __xpg_basename
>
> in libgen.h.
>
> The below patch emulates the way glibc does it. I tested it locally
> on a Cygwin installation, but I wouldn't be too unhappy for some
> scrutinizing look.
Looks nominally sound, but some suggested tweaks, below.
>
>
> Thanks,
> Corinna
>
>
> * libc/include/libgen.h: Drop defining _BASENAME_DEFINED. Always
> define macro basename. Add comment to explain why.
> * libc/include/string.h: Check for basename instead of
> _BASENAME_DEFINED. Drop __GNUC__ branch and always use basename
> macro instead. Change comment to explain why.
>
>
> diff --git a/newlib/libc/include/libgen.h b/newlib/libc/include/libgen.h
> index 8360a22..292d585 100644
> --- a/newlib/libc/include/libgen.h
> +++ b/newlib/libc/include/libgen.h
> @@ -12,8 +12,14 @@
> extern "C" {
> #endif
>
> +/* There are two common basename variants. If you #include <libgen.h> you get
> + the POSIX version; otherwise, if you define _GNU_SOURCE, you get the GNU
> + version via <string.h>. POSIX requires that #undef basename will still let
> + you invoke the underlying function. However, this also implies that the
> + POSIX version is used in this case. That's made sure here. */
> +#undef basename
> +#define basename basename
> char *_EXFUN(basename, (char *));
> -#define _BASENAME_DEFINED
> char *_EXFUN(dirname, (char *));
>
> #ifdef __cplusplus
> diff --git a/newlib/libc/include/string.h b/newlib/libc/include/string.h
> index 9e11e5c..cee9bd9 100644
> --- a/newlib/libc/include/string.h
> +++ b/newlib/libc/include/string.h
> @@ -163,18 +163,14 @@ int _EXFUN(strtosigno, (const char *__name));
> (char *) memcpy (__out, __in, __len-1);}))
> #endif /* _GNU_SOURCE && __GNUC__ */
>
> -/* There are two common basename variants. If you #include <libgen.h>
> - first, you get the POSIX version; otherwise you get the GNU version.
> - POSIX requires that #undef basename will still let you
> - invoke the underlying function, but that requires gcc support. */
> -#if __GNU_VISIBLE && !defined(_BASENAME_DEFINED)
> -# ifdef __GNUC__
> -char *_EXFUN(basename,(const char *))
> - __asm__ (__ASMNAME ("__gnu_basename")) __nonnull(1);
> -# else
> +/* There are two common basename variants. If you #include <libgen.h> you get
> + the POSIX version; otherwise, if you define _GNU_SOURCE, you get the GNU
> + version via <string.h>. POSIX requires that #undef basename will still let
> + you invoke the underlying function. However, this also implies that the
> + POSIX version is used in this case. That's made sure here. */
> +#if __GNU_VISIBLE && !defined(basename)
> char *_EXFUN(__gnu_basename,(const char *));
> # define basename __gnu_basename
> -# endif
> #endif
The prototype should not be skipped if basename is defined (which I'm guessing
is an unintended change). In addition, to help make use more clear
(particularly for users--maintainers too, but not so much) the comment and #if
condition should perhaps be changed a little. I suggest it should end up being
more like the following. (Comment same in both files, as you have it, but only
shown once in the context of string.h.)
I also added the nonnull attribute to the prototype, to avoid a possible change
in compiler warnings due to the GNUC part being dropped.
/* There are two common basename variants. If you do NOT #include <libgen.h>
* and you do
* #define _GNU_SOURCE
* #include <string.h>
* you get the GNU version. Otherwise, you get the POSIX version, for which
* you should #include <libgen.h> for the function prototype. POSIX requires
* that #undef basename will still let you invoke the underlying function.
* However, this also implies that the POSIX version is used in this case.
* That's made sure here. */
#if defined(_GNU_SOURCE)
char *_EXFUN(__nonnull(1) __gnu_basename,(const char *));
# if !defined(basename)
# define basename __gnu_basename
# endif
#endif
(I suggest #if defined(_GNU_SOURCE) instead of # if __GNU_VISIBLE because the former is the method by which the user is supposed to request it. Yes, given cdefs.h the two are equivalent, but it seems that _GNU_SOURCE would be more clear. (No need to spend time figuring out the __GNU_VISIBLE indirection.))
Craig
More information about the Newlib
mailing list