From 09c39d6df0e952620f8c1751377b559a04e023aa Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Tue, 19 Aug 2014 14:30:30 -0700 Subject: [PATCH] Implement the GNU basename(3) in addition to the POSIX one. Code like perf(1) needs this. Bug: 11860789 Change-Id: I907eb448052a7b165e4012d74303330d32328cb2 --- libc/Android.mk | 1 + libc/bionic/__gnu_basename.cpp | 35 ++++++++++++++++++++++++++++++++++ libc/include/libgen.h | 12 +++++++++++- libc/include/string.h | 21 +++++++++++++++++--- tests/libgen_test.cpp | 5 ++--- tests/string_test.cpp | 23 ++++++++++++++++++++-- 6 files changed, 88 insertions(+), 9 deletions(-) create mode 100644 libc/bionic/__gnu_basename.cpp diff --git a/libc/Android.mk b/libc/Android.mk index ca22bbcf6..937f741d5 100644 --- a/libc/Android.mk +++ b/libc/Android.mk @@ -122,6 +122,7 @@ libc_bionic_src_files := \ bionic/getpgrp.cpp \ bionic/getpid.cpp \ bionic/gettid.cpp \ + bionic/__gnu_basename.cpp \ bionic/inotify_init.cpp \ bionic/lchown.cpp \ bionic/lfs64_support.cpp \ diff --git a/libc/bionic/__gnu_basename.cpp b/libc/bionic/__gnu_basename.cpp new file mode 100644 index 000000000..1eb3f6594 --- /dev/null +++ b/libc/bionic/__gnu_basename.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#define _GNU_SOURCE 1 +#include + +extern "C" const char* __gnu_basename(const char* path) { + const char* last_slash = strrchr(path, '/'); + return (last_slash != NULL) ? last_slash + 1 : path; +} diff --git a/libc/include/libgen.h b/libc/include/libgen.h index c5fc76a79..4caf8b9c5 100644 --- a/libc/include/libgen.h +++ b/libc/include/libgen.h @@ -25,6 +25,7 @@ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ + #ifndef _LIBGEN_H #define _LIBGEN_H @@ -33,9 +34,18 @@ __BEGIN_DECLS +#if !defined(__bionic_using_gnu_basename) +/* + * gets you the GNU basename. + * the POSIX one. + * Note that our "POSIX" one has the wrong argument cv-qualifiers. + */ +extern char* basename(const char*); +#define __bionic_using_posix_basename +#endif + /* our version of dirname/basename don't modify the input path */ extern char* dirname (const char* path); -extern char* basename(const char* path); /* special thread-safe Bionic versions * diff --git a/libc/include/string.h b/libc/include/string.h index 643132d67..0a1d653b8 100644 --- a/libc/include/string.h +++ b/libc/include/string.h @@ -25,8 +25,9 @@ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#ifndef _STRING_H_ -#define _STRING_H_ + +#ifndef _STRING_H +#define _STRING_H #include #include @@ -92,6 +93,20 @@ extern size_t strxfrm(char* __restrict, const char* __restrict, size_t); extern int strcoll_l(const char *, const char *, locale_t) __purefunc; extern size_t strxfrm_l(char* __restrict, const char* __restrict, size_t, locale_t); +#if defined(__USE_GNU) && !defined(__bionic_using_posix_basename) +/* + * glibc has a basename in that's different to the POSIX one in . + * It doesn't modify its argument, and in C++ it's const-correct. + */ +#if defined(__cplusplus) +extern "C++" char* basename(char*) __RENAME(__gnu_basename) __nonnull((1)); +extern "C++" const char* basename(const char*) __RENAME(__gnu_basename) __nonnull((1)); +#else +extern char* basename(const char*) __RENAME(__gnu_basename) __nonnull((1)); +#endif +#define __bionic_using_gnu_basename +#endif + #if defined(__BIONIC_FORTIFY) __BIONIC_FORTIFY_INLINE @@ -289,4 +304,4 @@ char* strrchr(const char *s, int c) { __END_DECLS -#endif /* _STRING_H_ */ +#endif /* _STRING_H */ diff --git a/tests/libgen_test.cpp b/tests/libgen_test.cpp index cae646f25..d0402dbf6 100644 --- a/tests/libgen_test.cpp +++ b/tests/libgen_test.cpp @@ -14,11 +14,10 @@ * limitations under the License. */ -#include - #include #include +#include static void TestBasename(const char* in, const char* expected_out) { char* writable_in = (in != NULL) ? strdup(in) : NULL; @@ -40,7 +39,7 @@ static void TestDirname(const char* in, const char* expected_out) { // Do not use basename as the test name, it's defined to another value in glibc // so leads to a differently named test on host versus target architectures. -TEST(libgen, basename_smoke) { +TEST(libgen, posix_basename) { TestBasename(NULL, "."); TestBasename("", "."); TestBasename("/usr/lib", "lib"); diff --git a/tests/string_test.cpp b/tests/string_test.cpp index 73c94c602..a3c6abb7d 100644 --- a/tests/string_test.cpp +++ b/tests/string_test.cpp @@ -14,12 +14,12 @@ * limitations under the License. */ -#include +#include #include +#include #include #include -#include #include "buffer_tests.h" @@ -1287,3 +1287,22 @@ TEST(string, strchr_align) { TEST(string, strchr_overread) { RunSingleBufferOverreadTest(DoStrchrTest); } + +static void TestBasename(const char* in, const char* expected_out) { + errno = 0; + const char* out = basename(in); + ASSERT_STREQ(expected_out, out) << in; + ASSERT_EQ(0, errno) << in; +} + +TEST(string, __gnu_basename) { + TestBasename("", ""); + TestBasename("/usr/lib", "lib"); + TestBasename("/usr/", ""); + TestBasename("usr", "usr"); + TestBasename("/", ""); + TestBasename(".", "."); + TestBasename("..", ".."); + TestBasename("///", ""); + TestBasename("//usr//lib//", ""); +}