7

How do I use the GNU C Library version of basename() and dirname()?.

If you

#include <libgen.h>

for dirname You're already getting the POSIX, not the GNU, version of basename(). (Even if you

#define _GNU_SOURCE

As far as I know there is no conditional importing in C. Is there a gcc specific trick?

PeeHaa
  • 66,697
  • 53
  • 182
  • 254
Roman A. Taycher
  • 16,401
  • 19
  • 81
  • 129

5 Answers5

10

Just write it yourself and give it a different name than basename. This GNU insistence on creating alternate non-conforming versions of standard functions that can be written in 1-3 lines is completely batty.

char *gnu_basename(char *path)
{
    char *base = strrchr(path, '/');
    return base ? base+1 : path;
}

This way, your program will also be more portable.

R.. GitHub STOP HELPING ICE
  • 195,354
  • 31
  • 331
  • 669
  • 3
    And your trivial implementation will have problems with "/" and "/home/mine/". – Jonathan Leffler Apr 27 '11 at 13:33
  • 6
    That is the behavior specified for the GNU version of the function! See http://www.kernel.org/doc/man-pages/online/pages/man3/basename.3.html "The GNU version never modifies its argument, and returns the empty string when path has a trailing slash, and in particular also when it is "/"." If you don't want the broken GNU behavior, don't ask for it... – R.. GitHub STOP HELPING ICE Apr 27 '11 at 13:34
  • 1
    OK - there had better be a good reason for the GNU to go off on its own tangent. I'll make a note of that peculiarity. I can sympathize with 'not modifying the argument'. I have my own prefixed version that gets a sized buffer to copy the basename into, and it has a `const char *` argument for the input. – Jonathan Leffler Apr 27 '11 at 13:40
  • 2
    For functions like this, it really just makes the most sense to write your own with the exact semantics you expect. – R.. GitHub STOP HELPING ICE Apr 27 '11 at 13:46
  • 1
    @Jonathan Leffler Forgive me if I've misunderstood, but I think Roman is asking how to ensure the GNU version of these functions are used. I'm having a similar issue -- in that path is being modified even though libgen.h is not included. As R.. mentioned, it's simple to write your own, however I don't understand why I would do this if the GNU versions do what I need? – J. Andrew Laughlin Jun 17 '11 at 14:36
  • @J.AndrewLaughlin In cases like this you write your own one because if the source changes, the compiler may include the wrong _basename()_ without giving you any notice, breaking the code that worked and did not change. – 18446744073709551615 Jul 23 '14 at 09:01
3

According to the man page you should do

      #define _GNU_SOURCE
       #include <string.h>
       #include <libgen.h>

If you get the POSIX version, libgen.h is probably already included before that point. You may want to include -D_GNU_SOURCE in the CPPFLAGS for compilation:

gcc -D_GNU_SOURCE ....
sehe
  • 328,274
  • 43
  • 416
  • 565
  • simplified more specific answer – sehe Apr 27 '11 at 10:01
  • 3 lines gives me __xpg_basename@@GLIBC_2.0, no libgen gives me basename@@GLIBC_2.0 (courtesy of nm). not defining GNU_SO URCE gives me basename@@GLIBC_2.0 but warns of initialization makes pointer from integer without a cast – Roman A. Taycher Apr 27 '11 at 10:23
  • I think -D_GNU_SOURCE just does the same as defining _GNU_SOURCE and it warns if I do both. – Roman A. Taycher Apr 27 '11 at 10:28
  • @Roman: -D_GNU_SOURCE does the same _BUT_ at a different time. This is exactly the difference I'm talking about. The pointer conversion warning, is just the pointer conversion warning. You can fix that by provining the expected argument type. – sehe Apr 27 '11 at 10:36
1

After examining libgen.h, I'm pretty sure I have a warning-free and error-free solution:

/* my C program */
#define _GNU_SOURCE     /*  for GNU version of basename(3) */
#include <libgen.h>     /*  for dirname(3) */
#undef basename         /*  (snide comment about libgen.h removed) */
#include <string.h>     /*  for basename(3) (GNU version) and strcmp(3) */

/* rest of C program... */

With the #undef line, now my program includes dirname(3) from libgen.h and the GNU version of basename(3) from string.h.

No compiler warnings/errors from either gcc (version 4.5.2) or clang (version 3.3).

pr1268
  • 1,148
  • 3
  • 9
  • 15
0

It's crazy basename and dirname have two versions.

We worked at a big project, it looks like these two apis already caused potentially bugs. So we marked "basename" "dirname" as deprecated for warning if someone use it:

#ifdef basename
__attribute__ ((deprecated))
char *__xpg_basename(char *path);
#else
__attribute__ ((deprecated))
char *basename(const char *path);
#endif

 __attribute__ ((deprecated))
char *dirname(char *path);

We also try to introduce a base c foundation library such as glib or libcork, but it looks like too heavy. So we write a tiny library for this purpose, it implementation like this:

#include <libgen.h>       // for dirname
#include <linux/limits.h> // for PATH_MAX
#include <stdio.h>        // for snprintf
#include <string.h>       // for basename
#include <stdbool.h>      // for bool

bool get_basename(const char *path, char *name, size_t name_size) {
  char path_copy[PATH_MAX] = {'\0'};
  strncpy(path_copy, path, sizeof(path_copy) - 1);
  return snprintf(name, name_size, "%s", basename(path_copy)) < name_size;
}

bool get_dirname(const char *path, char *name, size_t name_size) {
  char path_copy[PATH_MAX] = {'\0'};
  strncpy(path_copy, path, sizeof(path_copy) - 1);
  return snprintf(name, name_size, "%s", dirname(path_copy)) < name_size;
}

Then we replace all basename dirname call with get_basename get_dirname.

tangxinfa
  • 1,142
  • 10
  • 12
0

Make sure you're building with the GNU C library, rather than your system's (presumed) POSIX-compatible default.

This is often set in the GCC spec file. Use the -v option to show the current settings:

$ gcc -v
Using built-in specs.
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu/Linaro 4.4.4-14ubuntu5' --with-bugurl=file:///usr/share/doc/gcc-4.4/README.Bugs --enable-languages=c,c++,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.4 --enable-shared --enable-multiarch --enable-linker-build-id --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.4 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-objc-gc --disable-werror --with-arch-32=i686 --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 4.4.5 (Ubuntu/Linaro 4.4.4-14ubuntu5)
unwind
  • 364,555
  • 61
  • 449
  • 578