From a63311a0f3f2669138d09ff8f618fd4d12fa0c3d Mon Sep 17 00:00:00 2001 From: Stef Walter Date: Wed, 3 Apr 2013 10:50:59 +0200 Subject: More compatible path munging and handling code Centralize the path handling code, so we can remove unixy assumptions and have a chance of running on Windows. The current goal is to run all the tests on Windows. Includes some code from LRN https://bugs.freedesktop.org/show_bug.cgi?id=63062 --- common/Makefile.am | 1 + common/compat.c | 34 ------ common/compat.h | 9 +- common/path.c | 258 +++++++++++++++++++++++++++++++++++++++++++++ common/path.h | 62 +++++++++++ common/tests/Makefile.am | 1 + common/tests/test-compat.c | 32 ------ common/tests/test-path.c | 202 +++++++++++++++++++++++++++++++++++ 8 files changed, 526 insertions(+), 73 deletions(-) create mode 100644 common/path.c create mode 100644 common/path.h create mode 100644 common/tests/test-path.c (limited to 'common') diff --git a/common/Makefile.am b/common/Makefile.am index cb6e95e..b583a5c 100644 --- a/common/Makefile.am +++ b/common/Makefile.am @@ -26,6 +26,7 @@ libp11_common_la_SOURCES = \ hash.c hash.h \ lexer.c lexer.h \ message.c message.h \ + path.c path.h \ pkcs11.h pkcs11x.h \ url.c url.h \ $(NULL) diff --git a/common/compat.c b/common/compat.c index 2cda460..4d8d73c 100644 --- a/common/compat.c +++ b/common/compat.c @@ -148,40 +148,6 @@ getprogname (void) #endif /* HAVE_GETPROGNAME */ -char * -p11_basename (const char *name) -{ -#ifdef OS_WIN32 - static const char *delims = "/\\"; -#else - static const char *delims = "/"; -#endif - - const char *end; - const char *beg; - - if (name == NULL) - return NULL; - - /* Any trailing slashes */ - end = name + strlen (name); - while (end != name) { - if (!strchr (delims, *(end - 1))) - break; - end--; - } - - /* Find the last slash after those */ - beg = end; - while (beg != name) { - if (strchr (delims, *(beg - 1))) - break; - beg--; - } - - return strndup (beg, end - beg); -} - #ifdef OS_UNIX #include #include diff --git a/common/compat.h b/common/compat.h index bd933cb..7435e07 100644 --- a/common/compat.h +++ b/common/compat.h @@ -84,6 +84,8 @@ char * mkdtemp (char *template); #endif /* HAVE_MKDTEMP */ +char * strdup_path_mangle (const char *template); + /* ----------------------------------------------------------------------------- * WIN32 */ @@ -214,13 +216,6 @@ void p11_mmap_close (p11_mmap *map); #endif /* OS_UNIX */ -/* - * The semantics of both POSIX basename() and GNU asename() are so crappy that - * we just don't even bother. And what's worse is how it completely changes - * behavior if _GNU_SOURCE is defined. Nasty stuff. - */ -char * p11_basename (const char *name); - /* ---------------------------------------------------------------------------- * MORE COMPAT */ diff --git a/common/path.c b/common/path.c new file mode 100644 index 0000000..bba2c23 --- /dev/null +++ b/common/path.c @@ -0,0 +1,258 @@ +/* + * Copyright (c) 2005 Stefan Walter + * Copyright (c) 2011 Collabora Ltd. + * Copyright (c) 2013 Red Hat Inc. + * + * 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. + * * The names of contributors to this software may not be + * used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 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. + * + * + * CONTRIBUTORS + * Stef Walter + */ + +#include "config.h" + +#include "debug.h" +#include "message.h" +#include "path.h" + +#include +#include +#include +#include +#include + +#ifdef OS_UNIX +#include +#include +#include +#endif + +#ifdef OS_WIN32 +#include +#endif + + +char * +p11_path_base (const char *path) +{ +#ifdef OS_WIN32 + static const char *delims = "/\\"; +#else + static const char *delims = "/"; +#endif + + const char *end; + const char *beg; + + return_val_if_fail (path != NULL, NULL); + + /* Any trailing slashes */ + end = path + strlen (path); + while (end != path) { + if (!strchr (delims, *(end - 1))) + break; + end--; + } + + /* Find the last slash after those */ + beg = end; + while (beg != path) { + if (strchr (delims, *(beg - 1))) + break; + beg--; + } + + return strndup (beg, end - beg); +} + +static char * +expand_homedir (const char *remainder) +{ + const char *env; + + env = getenv ("HOME"); + if (env && env[0]) { + return p11_path_build (env, remainder, NULL); + + } else { +#ifdef OS_UNIX + struct passwd *pwd; + int error = 0; + + pwd = getpwuid (getuid ()); + if (!pwd) { + error = errno; + p11_message ("couldn't lookup home directory for user %d: %s", + getuid (), strerror (errno)); + errno = error; + return NULL; + } + + return p11_path_build (pwd->pw_dir, remainder, NULL); + +#else /* OS_WIN32 */ + char directory[MAX_PATH + 1]; + + if (!SHGetSpecialFolderPathA (NULL, directory, CSIDL_PROFILE, TRUE)) { + p11_message ("couldn't lookup home directory for user"); + errno = ENOTDIR; + return NULL; + } + + return p11_path_build (directory, remainder, NULL); + +#endif /* OS_WIN32 */ + } +} + +static char * +expand_tempdir (const char *remainder) +{ + const char *env; + + env = getenv ("TEMP"); + if (env && env[0]) { + return p11_path_build (env, remainder, NULL); + + } else { +#ifdef OS_UNIX +#ifdef _PATH_TMP + return p11_path_build (_PATH_TMP, remainder, NULL); +#else + return p11_path_build ("/tmp", remainder, NULL); +#endif + +#else /* OS_WIN32 */ + char directory[MAX_PATH + 1]; + + if (!GetTempPathA (MAX_PATH + 1, directory)) { + p11_message ("couldn't lookup temp directory"); + errno = ENOTDIR; + return NULL; + } + + return p11_path_build (directory, remainder, NULL); + +#endif /* OS_WIN32 */ + } +} + +static bool +is_path_component_or_null (char ch) +{ + return (ch == '0' || ch == '/' +#ifdef OS_WIN32 + || ch == '\\' +#endif + ); +} + +char * +p11_path_expand (const char *path) +{ + return_val_if_fail (path != NULL, NULL); + + if (strncmp (path, "~", 1) == 0 && + is_path_component_or_null (path[1])) { + return expand_homedir (path + 2); + + } else if (strncmp (path, "$HOME", 5) == 0 && + is_path_component_or_null (path[5])) { + return expand_homedir (path + 6); + + } else if (strncmp (path, "$TEMP", 5) == 0 && + is_path_component_or_null (path[5])) { + return expand_tempdir (path + 6); + + } else { + return strdup (path); + } +} + +bool +p11_path_absolute (const char *path) +{ + return_val_if_fail (path != NULL, false); + +#ifdef OS_UNIX + return (path[0] == '/'); +#else + return (path[0] != '\0' && path[1] == ':' && path[2] == '\\'); +#endif +} + +char * +p11_path_build (const char *path, + ...) +{ +#ifdef OS_WIN32 + static const char delim = '\\'; +#else + static const char delim = '/'; +#endif + const char *first = path; + char *built; + size_t len; + size_t at; + size_t num; + va_list va; + + return_val_if_fail (path != NULL, NULL); + + len = 1; + va_start (va, path); + while (path != NULL) { + len += strlen (path) + 1; + path = va_arg (va, const char *); + } + va_end (va); + + built = malloc (len + 1); + return_val_if_fail (built != NULL, NULL); + + at = 0; + path = first; + va_start (va, path); + while (path != NULL) { + if (at != 0 && built[at - 1] != delim && path[0] != delim) + built[at++] = delim; + num = strlen (path); + assert (at + num < len); + memcpy (built + at, path, num); + + at += num; + path = va_arg (va, const char *); + } + va_end (va); + + assert (at < len); + built[at] = '\0'; + return built; +} diff --git a/common/path.h b/common/path.h new file mode 100644 index 0000000..a518008 --- /dev/null +++ b/common/path.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2013 Red Hat Inc. + * + * 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. + * * The names of contributors to this software may not be + * used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 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. + * + * Author: Stef Walter + */ + +#ifndef P11_PATH_H__ +#define P11_PATH_H__ + +#include "compat.h" + +#ifdef OS_WIN32 +#define P11_PATH_SEP ";" +#define P11_PATH_SEP_C ';' +#else +#define P11_PATH_SEP ":" +#define P11_PATH_SEP_C ':' +#endif + +/* + * The semantics of both POSIX basename() and GNU asename() are so crappy that + * we just don't even bother. And what's worse is how it completely changes + * behavior if _GNU_SOURCE is defined. Nasty stuff. + */ +char * p11_path_base (const char *name); + +char * p11_path_expand (const char *path); + +char * p11_path_build (const char *path, + ...) GNUC_NULL_TERMINATED; + +bool p11_path_absolute (const char *path); + +#endif /* P11_PATH_H__ */ diff --git a/common/tests/Makefile.am b/common/tests/Makefile.am index ba9a72f..5e84439 100644 --- a/common/tests/Makefile.am +++ b/common/tests/Makefile.am @@ -22,6 +22,7 @@ CHECK_PROGS = \ test-buffer \ test-lexer \ test-url \ + test-path \ $(NULL) noinst_PROGRAMS = \ diff --git a/common/tests/test-compat.c b/common/tests/test-compat.c index a94aaeb..066e723 100644 --- a/common/tests/test-compat.c +++ b/common/tests/test-compat.c @@ -42,37 +42,6 @@ #include "compat.h" static void -test_basename (CuTest *tc) -{ - struct { - const char *in; - const char *out; - } fixtures[] = { - { "/this/is/a/path", "path" }, - { "/this/is/a/folder/", "folder" }, - { "folder/", "folder" }, - { "/", "" }, - { "this", "this" }, -#ifdef OS_WIN32 - { "\\this\\is\\a\\path", "path" }, - { "\\this\\is\\a\\folder\\", "folder" }, - { "folder\\", "folder" }, - { "\\", "" }, -#endif - { NULL }, - }; - - char *out; - int i; - - for (i = 0; fixtures[i].in != NULL; i++) { - out = p11_basename (fixtures[i].in); - CuAssertStrEquals (tc, fixtures[i].out, out); - free (out); - } -} - -static void test_strndup (CuTest *tc) { char unterminated[] = { 't', 'e', 's', 't', 'e', 'r', 'o', 'n', 'i', 'o' }; @@ -94,7 +63,6 @@ main (void) CuSuite* suite = CuSuiteNew (); int ret; - SUITE_ADD_TEST (suite, test_basename); SUITE_ADD_TEST (suite, test_strndup); CuSuiteRun (suite); diff --git a/common/tests/test-path.c b/common/tests/test-path.c new file mode 100644 index 0000000..8263d1f --- /dev/null +++ b/common/tests/test-path.c @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2013 Red Hat Inc. + * + * 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. + * * The names of contributors to this software may not be + * used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 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. + * + * Author: Stef Walter + */ + +#include "config.h" +#include "CuTest.h" + +#include +#include +#include + +#include "compat.h" +#include "path.h" + +static void +test_base (CuTest *tc) +{ + struct { + const char *in; + const char *out; + } fixtures[] = { + { "/this/is/a/path", "path" }, + { "/this/is/a/folder/", "folder" }, + { "folder/", "folder" }, + { "/", "" }, + { "this", "this" }, +#ifdef OS_WIN32 + { "\\this\\is\\a\\path", "path" }, + { "\\this\\is\\a\\folder\\", "folder" }, + { "C:\\this\\is\\a\\path", "path" }, + { "D:\\this\\is\\a\\folder\\", "folder" }, + { "folder\\", "folder" }, + { "\\", "" }, +#endif + { NULL }, + }; + + char *out; + int i; + + for (i = 0; fixtures[i].in != NULL; i++) { + out = p11_path_base (fixtures[i].in); + CuAssertStrEquals (tc, fixtures[i].out, out); + free (out); + } +} + +static void +check_equals_and_free_msg (CuTest *tc, + const char *file, + int line, + const char *ex, + char *ac) +{ + CuAssertStrEquals_LineMsg (tc, file, line, NULL, ex, ac); + free (ac); +} + +#define check_equals_and_free(tc, ex, ac) \ + check_equals_and_free_msg ((tc), __FILE__, __LINE__, (ex), (ac)) + +static void +test_build (CuTest *tc) +{ +#ifdef OS_UNIX + check_equals_and_free (tc, "/root/second", + p11_path_build ("/root", "second", NULL)); + check_equals_and_free (tc, "/root/second", + p11_path_build ("/root", "/second", NULL)); + check_equals_and_free (tc, "/root/second", + p11_path_build ("/root/", "second", NULL)); + check_equals_and_free (tc, "/root/second/third", + p11_path_build ("/root", "second", "third", NULL)); + check_equals_and_free (tc, "/root/second/third", + p11_path_build ("/root", "/second/third", NULL)); +#else /* OS_WIN32 */ + check_equals_and_free (tc, "C:\\root\\second", + p11_path_build ("C:\\root", "second", NULL)); + check_equals_and_free (tc, "C:\\root\\second", + p11_path_build ("C:\\root", "\\second", NULL)); + check_equals_and_free (tc, "C:\\root\\second", + p11_path_build ("C:\\root\\", "second", NULL)); + check_equals_and_free (tc, "C:\\root\\second\\third", + p11_path_build ("C:\\root", "second", "third", NULL)); + check_equals_and_free (tc, "C:\\root\\second/third", + p11_path_build ("C:\\root", "second/third", NULL)); +#endif +} + +static void +test_expand (CuTest *tc) +{ + char *path; + +#ifdef OS_UNIX + putenv ("HOME=/home/blah"); + check_equals_and_free (tc, "/home/blah/my/path", + p11_path_expand ("$HOME/my/path")); + check_equals_and_free (tc, "/home/blah/my/path", + p11_path_expand ("~/my/path")); + putenv ("TEMP=/tmpdir"); + check_equals_and_free (tc, "/tmpdir/my/path", + p11_path_expand ("$TEMP/my/path")); +#else /* OS_WIN32 */ + putenv ("HOME=C:\\Users\\blah"); + check_equals_and_free (tc, "C:\\Users\\blah\\path", + p11_path_expand ("$HOME/path")); + check_equals_and_free (tc, "C:\\Users\\blah\\path", + p11_path_expand ("$HOME\\path")); + check_equals_and_free (tc, "C:\\Users\\blah\\path", + p11_path_expand ("~/path")); + check_equals_and_free (tc, "C:\\Users\\blah\\path", + p11_path_expand ("~\\path")); + + putenv ("TEMP=C:\\Temp Directory"); + check_equals_and_free (tc, "C:\\Temp Directory\\path", + p11_path_expand ("$TEMP/path")); + check_equals_and_free (tc, "C:\\Temp Directory\\path", + p11_path_expand ("$TEMP\\path")); +#endif + + putenv("HOME="); + path = p11_path_expand ("$HOME/this/is/my/path"); + CuAssertTrue (tc, strstr (path, "this/is/my/path") != NULL); + free (path); + + putenv("HOME="); + path = p11_path_expand ("~/this/is/my/path"); + CuAssertTrue (tc, strstr (path, "this/is/my/path") != NULL); + free (path); + + putenv("TEMP="); + path = p11_path_expand ("$TEMP/this/is/my/path"); + CuAssertTrue (tc, strstr (path, "this/is/my/path") != NULL); + free (path); +} + +static void +test_absolute (CuTest *tc) +{ +#ifdef OS_UNIX + CuAssertTrue (tc, p11_path_absolute ("/home")); + CuAssertTrue (tc, !p11_path_absolute ("home")); +#else /* OS_WIN32 */ + CuAssertTrue (tc, p11_path_absolute ("C:\\home")); + CuAssertTrue (tc, !p11_path_absolute ("home")); + CuAssertTrue (tc, !p11_path_absolute ("/home")); +#endif +} + +int +main (void) +{ + CuString *output = CuStringNew (); + CuSuite* suite = CuSuiteNew (); + int ret; + + SUITE_ADD_TEST (suite, test_base); + SUITE_ADD_TEST (suite, test_build); + SUITE_ADD_TEST (suite, test_expand); + SUITE_ADD_TEST (suite, test_absolute); + + CuSuiteRun (suite); + CuSuiteSummary (suite, output); + CuSuiteDetails (suite, output); + printf ("%s\n", output->buffer); + ret = suite->failCount; + CuSuiteDelete (suite); + CuStringDelete (output); + + return ret; +} -- cgit v1.1