Move default library path search to program context
authorTeemu Murtola <teemu.murtola@gmail.com>
Sun, 12 Jan 2014 18:37:08 +0000 (20:37 +0200)
committerGerrit Code Review <gerrit@gerrit.gromacs.org>
Sat, 1 Mar 2014 16:47:04 +0000 (17:47 +0100)
The default library path search is now implemented in
CommandLineProgramContext, and accessible through the generic interface.
This provides the following benefits:
 - All the magic for finding stuff from the binary path is in a single
   place, under the commandline/ module, where it belongs.
 - Code using the library path is now in C++, fixing some potential
   buffer overruns and removing the need for some utility code.
 - Programs using the Gromacs library can customize how the library data
   files are found.
 - PATH_SEPARATOR is no longer exposed outside path.cpp, encapsulating
   this detail.
 - The library path is only searched once during the execution of the
   binary, reducing the amount of file I/O done if multiple data files
   are accessed.
 - The library path is printed in the startup header, making it explicit
   where they are read (helpful in particular if there are problems with
   it, or the user may have multiple installations).  The GMXLIB
   override doesn't currently affect this printout; maybe it should, but
   I don't want to put the environment variable (and the support for
   multiple directories) into the program context.

Change-Id: I4d099215ef7e1f269d4c7a42989835d980bc1566

12 files changed:
src/gromacs/commandline/cmdlineprogramcontext.cpp
src/gromacs/commandline/cmdlineprogramcontext.h
src/gromacs/fileio/futil.cpp
src/gromacs/fileio/futil.h
src/gromacs/gmxlib/copyrite.cpp
src/gromacs/gmxlib/string2.c
src/gromacs/gmxpreprocess/fflibutil.cpp
src/gromacs/legacyheaders/string2.h
src/gromacs/utility/path.cpp
src/gromacs/utility/path.h
src/gromacs/utility/programcontext.cpp
src/gromacs/utility/programcontext.h

index 12c73ece4d3ded46a2c5e741d48be958a3ed7ee9..ce66daf88a72072d6c456c977bb6f6380d3bc9a8 100644 (file)
@@ -164,6 +164,121 @@ std::string findFullBinaryPath(const std::string                    &invokedName
     return searchName;
 }
 
+/*! \brief
+ * Returns whether given path contains files from `share/top/`.
+ *
+ * Only checks for a single file that has an uncommon enough name.
+ */
+bool isAcceptableLibraryPath(const std::string &path)
+{
+    return Path::exists(Path::join(path, "gurgle.dat"));
+}
+
+/*! \brief
+ * Returns whether given path prefix contains files from `share/top/`.
+ *
+ * \param[in]  path   Path prefix to check.
+ * \param[out] result If return value is `true`, the pointee is set to the
+ *     actual data directory. Otherwise, the pointee is not modified.
+ * \returns  `true` if \p path contains the data files.
+ *
+ * Checks whether \p path could be the installation prefix where `share/top/`
+ * files have been installed:  appends the relative installation path of the
+ * data files and calls isAcceptableLibraryPath().
+ */
+bool isAcceptableLibraryPathPrefix(const std::string &path, std::string *result)
+{
+    std::string testPath = Path::join(path, GMXLIB_SEARCH_DIR);
+    if (isAcceptableLibraryPath(testPath))
+    {
+        *result = testPath;
+        return true;
+    }
+    return false;
+}
+
+/*! \brief
+ * Returns a fallback data path.
+ *
+ * Checks a few standard locations for the data files before returning a
+ * configure-time hard-coded path.  The hard-coded path is preferred if it
+ * actually contains the data files, though.
+ */
+std::string findFallbackLibraryDataPath()
+{
+#ifndef GMX_NATIVE_WINDOWS
+    if (!isAcceptableLibraryPath(GMXLIB_FALLBACK))
+    {
+        std::string foundPath;
+        if (isAcceptableLibraryPathPrefix("/usr/local", &foundPath))
+        {
+            return foundPath;
+        }
+        if (isAcceptableLibraryPathPrefix("/usr", &foundPath))
+        {
+            return foundPath;
+        }
+        if (isAcceptableLibraryPathPrefix("/opt", &foundPath))
+        {
+            return foundPath;
+        }
+    }
+#endif
+    return GMXLIB_FALLBACK;
+}
+
+/*! \brief
+ * Finds the library data files based on path of the binary.
+ *
+ * \param[in] binaryPath  Absolute path to the binary.
+ * \returns  Path to the `share/top/` data files.
+ *
+ * The search based on the path only works if the binary is in the same
+ * relative path as the installed \Gromacs binaries.  If the binary is
+ * somewhere else, a hard-coded fallback is used.  This doesn't work if the
+ * binaries are somewhere else than the path given during configure time...
+ *
+ * Extra logic is present to allow running binaries from the build tree such
+ * that they use up-to-date data files from the source tree.
+ */
+std::string findDefaultLibraryDataPath(const std::string &binaryPath)
+{
+    // If the input path is not absolute, the binary could not be found.
+    // Don't search anything.
+    if (Path::isAbsolute(binaryPath))
+    {
+        // Remove the executable name.
+        std::string searchPath = Path::getParentPath(binaryPath);
+        // If running directly from the build tree, try to use the source
+        // directory.
+#if (defined CMAKE_SOURCE_DIR && defined CMAKE_BINARY_DIR)
+        if (Path::startsWith(searchPath, CMAKE_BINARY_DIR))
+        {
+            std::string testPath = Path::join(CMAKE_SOURCE_DIR, "share/top");
+            if (isAcceptableLibraryPath(testPath))
+            {
+                return testPath;
+            }
+        }
+#endif
+
+        // Use the executable path to (try to) find the library dir.
+        while (!searchPath.empty())
+        {
+            std::string testPath = Path::join(searchPath, GMXLIB_SEARCH_DIR);
+            if (isAcceptableLibraryPath(testPath))
+            {
+                return testPath;
+            }
+            searchPath = Path::getParentPath(searchPath);
+        }
+    }
+
+    // End of smart searching. If we didn't find it in our parent tree,
+    // or if the program name wasn't set, return a fallback.
+    return findFallbackLibraryDataPath();
+}
+
 //! \}
 
 }   // namespace
@@ -179,12 +294,23 @@ class CommandLineProgramContext::Impl
         Impl(int argc, const char *const argv[],
              ExecutableEnvironmentPointer env);
 
+        /*! \brief
+         * Finds the full binary path if it isn't searched yet.
+         *
+         * Sets \a fullBinaryPath_ if it isn't set yet.
+         *
+         * The \a binaryPathMutex_ should be locked by the caller before
+         * calling this function.
+         */
+        void findBinaryPath() const;
+
         ExecutableEnvironmentPointer  executableEnv_;
         std::string                   invokedName_;
         std::string                   programName_;
         std::string                   displayName_;
         std::string                   commandLine_;
         mutable std::string           fullBinaryPath_;
+        mutable std::string           defaultLibraryDataPath_;
         mutable tMPI::mutex           binaryPathMutex_;
 };
 
@@ -197,9 +323,9 @@ CommandLineProgramContext::Impl::Impl(int argc, const char *const argv[],
                                       ExecutableEnvironmentPointer env)
     : executableEnv_(move(env))
 {
-    invokedName_          = (argc != 0 ? argv[0] : "");
-    programName_          = Path::splitToPathAndFilename(invokedName_).second;
-    programName_          = stripSuffixIfPresent(programName_, ".exe");
+    invokedName_ = (argc != 0 ? argv[0] : "");
+    programName_ = Path::getFilename(invokedName_);
+    programName_ = stripSuffixIfPresent(programName_, ".exe");
 
     commandLine_ = quoteIfNecessary(programName_.c_str());
     for (int i = 1; i < argc; ++i)
@@ -209,6 +335,19 @@ CommandLineProgramContext::Impl::Impl(int argc, const char *const argv[],
     }
 }
 
+void CommandLineProgramContext::Impl::findBinaryPath() const
+{
+    if (fullBinaryPath_.empty())
+    {
+        fullBinaryPath_ = findFullBinaryPath(invokedName_, *executableEnv_);
+        fullBinaryPath_ = Path::normalize(Path::resolveSymlinks(fullBinaryPath_));
+        // TODO: Investigate/Consider using a dladdr()-based solution.
+        // Potentially less portable, but significantly simpler, and also works
+        // with user binaries even if they are located in some arbitrary location,
+        // as long as shared libraries are used.
+    }
+}
+
 /********************************************************************
  * CommandLineProgramContext
  */
@@ -266,19 +405,20 @@ const char *CommandLineProgramContext::commandLine() const
 const char *CommandLineProgramContext::fullBinaryPath() const
 {
     tMPI::lock_guard<tMPI::mutex> lock(impl_->binaryPathMutex_);
-    if (impl_->fullBinaryPath_.empty())
+    impl_->findBinaryPath();
+    return impl_->fullBinaryPath_.c_str();
+}
+
+const char *CommandLineProgramContext::defaultLibraryDataPath() const
+{
+    tMPI::lock_guard<tMPI::mutex> lock(impl_->binaryPathMutex_);
+    if (impl_->defaultLibraryDataPath_.empty())
     {
-        impl_->fullBinaryPath_ =
-            Path::normalize(
-                    Path::resolveSymlinks(
-                            findFullBinaryPath(impl_->invokedName_,
-                                               *impl_->executableEnv_)));
-        // TODO: Investigate/Consider using a dladdr()-based solution.
-        // Potentially less portable, but significantly simpler, and also works
-        // with user binaries even if they are located in some arbitrary location,
-        // as long as shared libraries are used.
+        impl_->findBinaryPath();
+        impl_->defaultLibraryDataPath_ =
+            Path::normalize(findDefaultLibraryDataPath(impl_->fullBinaryPath_));
     }
-    return impl_->fullBinaryPath_.c_str();
+    return impl_->defaultLibraryDataPath_.c_str();
 }
 
 } // namespace gmx
index ab0d91d58f2dc534bf336663c934e861a60c1bdd..09b05bf300242a2c658f167d409c4c27b2bcc318 100644 (file)
@@ -186,21 +186,30 @@ class CommandLineProgramContext : public ProgramContextInterface
          */
         virtual const char *displayName() const;
         /*! \brief
-         * Returns the full command line used to invoke the binary.
+         * Returns the full path of the running binary.
          *
-         * Does not throw.
+         * \throws std::bad_alloc if out of memory.
+         * \throws tMPI::system_error on thread synchronization errors.
+         *
+         * Returns argv[0] if there was an error in finding the absolute path.
          */
-        virtual const char *commandLine() const;
-
+        virtual const char *fullBinaryPath() const;
         /*! \brief
-         * Returns the full path of the invoked binary.
+         * Returns the default path for \Gromacs data files.
          *
          * \throws std::bad_alloc if out of memory.
          * \throws tMPI::system_error on thread synchronization errors.
          *
-         * Returns argv[0] if there was an error in finding the absolute path.
+         * Returns a hardcoded path set during configuration time if there is
+         * an error in finding the library data files.
          */
-        virtual const char *fullBinaryPath() const;
+        virtual const char *defaultLibraryDataPath() const;
+        /*! \brief
+         * Returns the full command line used to invoke the binary.
+         *
+         * Does not throw.
+         */
+        virtual const char *commandLine() const;
 
     private:
         class Impl;
index 0f89c574ea671a6b596755d44ef28c493852aab2..bd435ec2bbcd9272f10e08339a0b52e394d74604 100644 (file)
@@ -734,158 +734,63 @@ gmx_directory_close(gmx_directory_t gmxdir)
 }
 
 
-static gmx_bool search_subdirs(const char *parent, char *libdir)
-{
-    char    *ptr;
-    gmx_bool found;
-
-    /* Search a few common subdirectory names for the gromacs library dir */
-    sprintf(libdir, "%s%cshare%ctop%cgurgle.dat", parent,
-            DIR_SEPARATOR, DIR_SEPARATOR, DIR_SEPARATOR);
-    found = gmx_fexist(libdir);
-    if (!found)
-    {
-        sprintf(libdir, "%s%c%s%cgurgle.dat", parent,
-                DIR_SEPARATOR, GMXLIB_SEARCH_DIR, DIR_SEPARATOR);
-        found = gmx_fexist(libdir);
-    }
-
-    /* Remove the gurgle.dat part from libdir if we found something */
-    if (found)
-    {
-        ptr  = strrchr(libdir, DIR_SEPARATOR); /* slash or backslash always present, no check necessary */
-        *ptr = '\0';
-    }
-    return found;
-}
-
-
-void get_libdir(char *libdir)
+char *low_gmxlibfn(const char *file, gmx_bool bAddCWD, gmx_bool bFatal)
 {
-    // TODO: There is a potential buffer overrun in the way libdir is passed in.
+    bool bEnvIsSet = false;
     try
     {
-        std::string fullPath = gmx::getProgramContext().fullBinaryPath();
-
-        // If running directly from the build tree, try to use the source
-        // directory.
-#if (defined CMAKE_SOURCE_DIR && defined CMAKE_BINARY_DIR)
-        // TODO: Consider adding Path::startsWith(), as this may not work as
-        // expected.
-        if (gmx::startsWith(fullPath, CMAKE_BINARY_DIR))
+        if (bAddCWD && gmx_fexist(file))
+        {
+            return gmx_strdup(file);
+        }
+        else
         {
-            if (search_subdirs(CMAKE_SOURCE_DIR, libdir))
+            std::string  libpath;
+            // GMXLIB can be a path.
+            const char  *lib = getenv("GMXLIB");
+            if (lib != NULL)
             {
-                return;
+                bEnvIsSet = true;
+                libpath   = lib;
+            }
+            else
+            {
+                libpath = gmx::getProgramContext().defaultLibraryDataPath();
             }
-        }
-#endif
 
-        // Remove the executable name
-        fullPath = gmx::Path::splitToPathAndFilename(fullPath).first;
-        // Now we have the full path to the gromacs executable.
-        // Use it to find the library dir.
-        while (!fullPath.empty())
-        {
-            if (search_subdirs(fullPath.c_str(), libdir))
+            std::vector<std::string>                 pathEntries;
+            gmx::Path::splitPathEnvironment(libpath, &pathEntries);
+            std::vector<std::string>::const_iterator i;
+            for (i = pathEntries.begin(); i != pathEntries.end(); ++i)
             {
-                return;
+                std::string testPath = gmx::Path::join(*i, file);
+                if (gmx::Path::exists(testPath))
+                {
+                    return gmx_strdup(testPath.c_str());
+                }
             }
-            fullPath = gmx::Path::splitToPathAndFilename(fullPath).first;
         }
     }
     GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
-
-    /* End of smart searching. If we didn't find it in our parent tree,
-     * or if the program name wasn't set, at least try some standard
-     * locations before giving up, in case we are running from e.g.
-     * a users home directory. This only works on unix or cygwin...
-     */
-    bool found = false;
-#ifndef GMX_NATIVE_WINDOWS
-    if (!found)
+    if (bFatal)
     {
-        found = search_subdirs("/usr/local", libdir);
-    }
-    if (!found)
-    {
-        found = search_subdirs("/usr", libdir);
-    }
-    if (!found)
-    {
-        found = search_subdirs("/opt", libdir);
-    }
-#endif
-    if (!found)
-    {
-        strcpy(libdir, GMXLIB_FALLBACK);
-    }
-}
-
-
-char *low_gmxlibfn(const char *file, gmx_bool bAddCWD, gmx_bool bFatal)
-{
-    char    *ret;
-    char    *lib, *dir;
-    char     buf[1024];
-    char     libpath[GMX_PATH_MAX];
-    gmx_bool env_is_set = FALSE;
-    char    *s, tmppath[GMX_PATH_MAX];
-
-    /* GMXLIB can be a path now */
-    lib = getenv("GMXLIB");
-    if (lib != NULL)
-    {
-        env_is_set = TRUE;
-        strncpy(libpath, lib, GMX_PATH_MAX);
-    }
-    else
-    {
-        get_libdir(libpath);
-    }
-
-    ret = NULL;
-    if (bAddCWD && gmx_fexist(file))
-    {
-        ret = gmx_strdup(file);
-    }
-    else
-    {
-        strncpy(tmppath, libpath, GMX_PATH_MAX);
-        s = tmppath;
-        while (ret == NULL && (dir = gmx_strsep(&s, PATH_SEPARATOR)) != NULL)
+        if (bEnvIsSet)
         {
-            sprintf(buf, "%s%c%s", dir, DIR_SEPARATOR, file);
-            if (gmx_fexist(buf))
-            {
-                ret = gmx_strdup(buf);
-            }
+            gmx_fatal(FARGS,
+                      "Library file %s not found %sin your GMXLIB path.",
+                      file, bAddCWD ? "in current dir nor " : "");
         }
-        if (ret == NULL && bFatal)
+        else
         {
-            if (env_is_set)
-            {
-                gmx_fatal(FARGS,
-                          "Library file %s not found %sin your GMXLIB path.",
-                          file, bAddCWD ? "in current dir nor " : "");
-            }
-            else
-            {
-                gmx_fatal(FARGS,
-                          "Library file %s not found %sin default directories.\n"
-                          "(You can set the directories to search with the GMXLIB path variable)",
-                          file, bAddCWD ? "in current dir nor " : "");
-            }
+            gmx_fatal(FARGS,
+                      "Library file %s not found %sin default directories.\n"
+                      "(You can set the directories to search with the GMXLIB path variable)",
+                      file, bAddCWD ? "in current dir nor " : "");
         }
     }
-
-    return ret;
+    return NULL;
 }
 
-
-
-
-
 FILE *low_libopen(const char *file, gmx_bool bFatal)
 {
     FILE *ff;
index 7d214e04dbb2eb9191e13dbc29f4b6406d30eaad..0e23661cd5480b67da5f0de13319ae1fe8e7b546 100644 (file)
@@ -51,16 +51,12 @@ extern "C" {
 
 /* Native windows uses backslash path separators.
  * Cygwin and everybody else in the world use slash.
- * When reading the PATH environment variable, Unix separates entries
- * with colon, while windows uses semicolon.
  */
 #include "../utility/gmx_header_config.h"
 #ifdef GMX_NATIVE_WINDOWS
 #define DIR_SEPARATOR '\\'
-#define PATH_SEPARATOR ";"
 #else
 #define DIR_SEPARATOR '/'
-#define PATH_SEPARATOR ":"
 #endif
 
 
@@ -167,9 +163,6 @@ int
 gmx_directory_close(gmx_directory_t gmxdir);
 
 
-
-void get_libdir(char *libdir);
-
 char *low_gmxlibfn(const char *file, gmx_bool bAddCWD, gmx_bool bFatal);
 
 FILE *low_libopen(const char *file, gmx_bool bFatal);
index 249810e6ab4115d6ce89569c4c4a6e65c10abf9e..2ecce78515bf73ba42823d7725d207f2e8afdd9a 100644 (file)
@@ -791,12 +791,17 @@ void printBinaryInformation(FILE                            *fp,
         printCopyright(fp);
         fprintf(fp, "\n");
     }
-    fprintf(fp, "%sGROMACS:    %s, %s%s%s\n", prefix, name,
+    fprintf(fp, "%sGROMACS:      %s, %s%s%s\n", prefix, name,
             GromacsVersion(), precisionString, suffix);
     const char *const binaryPath = programContext.fullBinaryPath();
     if (binaryPath != NULL && binaryPath[0] != '\0')
     {
-        fprintf(fp, "%sExecutable: %s%s\n", prefix, binaryPath, suffix);
+        fprintf(fp, "%sExecutable:   %s%s\n", prefix, binaryPath, suffix);
+    }
+    const char *const libraryPath = programContext.defaultLibraryDataPath();
+    if (libraryPath != NULL && libraryPath[0] != '\0')
+    {
+        fprintf(fp, "%sLibrary dir:  %s%s\n", prefix, libraryPath, suffix);
     }
     const char *const commandLine = programContext.commandLine();
     if (commandLine != NULL && commandLine[0] != '\0')
index f3d79ada1f3aaf3335da15834161c0d7208cf7fa..4fd00387d0bafebabaedfde38f6429a425dd7525 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -632,40 +632,3 @@ str_to_int64_t(const char *str, char **endptr)
     return _strtoi64(str, endptr, 10);
 #endif
 }
-
-char *gmx_strsep(char **stringp, const char *delim)
-{
-    char *ret;
-    int   len = strlen(delim);
-    int   i, j = 0;
-    int   found = 0;
-
-    if (!*stringp)
-    {
-        return NULL;
-    }
-    ret = *stringp;
-    do
-    {
-        if ( (*stringp)[j] == '\0')
-        {
-            found    = 1;
-            *stringp = NULL;
-            break;
-        }
-        for (i = 0; i < len; i++)
-        {
-            if ( (*stringp)[j] == delim[i])
-            {
-                (*stringp)[j] = '\0';
-                *stringp      = *stringp+j+1;
-                found         = 1;
-                break;
-            }
-        }
-        j++;
-    }
-    while (!found);
-
-    return ret;
-}
index b9928cdcf2edd84b5b652de0463fa5376cc21062..b77d817078b5d46e16950f59789f543eb52b0cd2 100644 (file)
 
 #include "fflibutil.h"
 
+#include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/path.h"
+#include "gromacs/utility/programcontext.h"
+
 const char *fflib_forcefield_dir_ext()
 {
     return ".ff";
@@ -137,109 +141,101 @@ static int low_fflib_search_file_end(const char *ffdir,
                                      char     ***filenames,
                                      char     ***filenames_short)
 {
-    char           *lib, *dir;
-    char           *libpath;
-    gmx_bool        env_is_set;
-    int             len_fe, len_name;
-    char          **fns, **fns_short;
-    char            dir_print[GMX_PATH_MAX];
-    char           *s, fn_dir[GMX_PATH_MAX];
-    gmx_directory_t dirhandle;
-    char            nextname[STRLEN];
-    int             n, n_thisdir, rc;
-
-    len_fe = strlen(file_end);
-
-    env_is_set = FALSE;
-    if (ffdir != NULL)
+    char **fns = NULL, **fns_short = NULL;
+    int    n   = 0;
+    try
     {
-        /* Search in current dir and ffdir */
-        libpath = gmxlibfn(ffdir);
-    }
-    else
-    {
-        /* GMXLIB can be a path now */
-        lib = getenv("GMXLIB");
-        snew(libpath, GMX_PATH_MAX);
-        if (bAddCWD)
-        {
-            sprintf(libpath, "%s%s", ".", PATH_SEPARATOR);
-        }
-        if (lib != NULL)
+        std::vector<std::string> libPaths;
+        bool                     bEnvIsSet = false;
+
+        if (ffdir != NULL)
         {
-            env_is_set = TRUE;
-            strncat(libpath, lib, GMX_PATH_MAX);
+            /* Search ffdir in current dir and library dirs */
+            libPaths.push_back(gmxlibfn(ffdir));
         }
         else
         {
-            get_libdir(libpath + strlen(libpath));
+            /* GMXLIB can be a path now */
+            if (bAddCWD)
+            {
+                libPaths.push_back(".");
+            }
+            const char *lib = getenv("GMXLIB");
+            if (lib != NULL)
+            {
+                bEnvIsSet = true;
+                gmx::Path::splitPathEnvironment(lib, &libPaths);
+            }
+            else
+            {
+                libPaths.push_back(gmx::getProgramContext().defaultLibraryDataPath());
+            }
         }
-    }
-    s         = libpath;
-    n         = 0;
-    fns       = NULL;
-    fns_short = NULL;
-    /* Loop over all the entries in libpath */
-    while ((dir = gmx_strsep(&s, PATH_SEPARATOR)) != NULL)
-    {
-        rc = gmx_directory_open(&dirhandle, dir);
-        if (rc == 0)
-        {
-            strcpy(dir_print, dir);
 
-            n_thisdir = 0;
-            while (gmx_directory_nextfile(dirhandle, nextname, STRLEN-1) == 0)
+        const int len_fe = strlen(file_end);
+
+        std::vector<std::string>::const_iterator i;
+        for (i = libPaths.begin(); i != libPaths.end(); ++i)
+        {
+            const char      *dir = i->c_str();
+            gmx_directory_t  dirhandle;
+            const int        rc  = gmx_directory_open(&dirhandle, dir);
+            if (rc == 0)
             {
-                nextname[STRLEN-1] = 0;
-                if (debug)
-                {
-                    fprintf(debug, "dir '%s' %d file '%s'\n",
-                            dir, n_thisdir, nextname);
-                }
-                len_name = strlen(nextname);
-                /* What about case sensitivity? */
-                if (len_name >= len_fe &&
-                    strcmp(nextname+len_name-len_fe, file_end) == 0)
+                char nextname[STRLEN];
+                int  n_thisdir = 0;
+                while (gmx_directory_nextfile(dirhandle, nextname, STRLEN-1) == 0)
                 {
-                    /* We have a match */
-                    srenew(fns, n+1);
-                    sprintf(fn_dir, "%s%c%s",
-                            dir_print, DIR_SEPARATOR, nextname);
+                    nextname[STRLEN-1] = 0;
+                    if (debug)
+                    {
+                        fprintf(debug, "dir '%s' %d file '%s'\n",
+                                dir, n_thisdir, nextname);
+                    }
+                    const int len_name = strlen(nextname);
+                    /* What about case sensitivity? */
+                    if (len_name >= len_fe &&
+                        strcmp(nextname+len_name-len_fe, file_end) == 0)
+                    {
+                        char fn_dir[GMX_PATH_MAX];
+                        /* We have a match */
+                        srenew(fns, n+1);
+                        sprintf(fn_dir, "%s%c%s", dir, DIR_SEPARATOR, nextname);
 
-                    /* Copy the file name, possibly including the path. */
-                    fns[n] = strdup(fn_dir);
+                        /* Copy the file name, possibly including the path. */
+                        fns[n] = strdup(fn_dir);
 
-                    if (ffdir == NULL)
-                    {
-                        /* We are searching in a path.
-                         * Use the relative path when we use share/top
-                         * from the installation.
-                         * Add the full path when we use the current
-                         * working directory of GMXLIB.
-                         */
-                        srenew(fns_short, n+1);
-                        if (strcmp(dir, ".") == 0 || env_is_set)
-                        {
-                            fns_short[n] = strdup(fn_dir);
-                        }
-                        else
+                        if (ffdir == NULL)
                         {
-                            fns_short[n] = strdup(nextname);
+                            /* We are searching in a path.
+                             * Use the relative path when we use share/top
+                             * from the installation.
+                             * Add the full path when we use the current
+                             * working directory of GMXLIB.
+                             */
+                            srenew(fns_short, n+1);
+                            if (strcmp(dir, ".") == 0 || bEnvIsSet)
+                            {
+                                fns_short[n] = strdup(fn_dir);
+                            }
+                            else
+                            {
+                                fns_short[n] = strdup(nextname);
+                            }
                         }
+                        n++;
+                        n_thisdir++;
                     }
-                    n++;
-                    n_thisdir++;
                 }
-            }
-            gmx_directory_close(dirhandle);
+                gmx_directory_close(dirhandle);
 
-            sort_filenames(n_thisdir,
-                           fns+n-n_thisdir,
-                           fns_short == NULL ? NULL : fns_short+n-n_thisdir);
+                sort_filenames(n_thisdir,
+                               fns+n-n_thisdir,
+                               fns_short == NULL ? NULL : fns_short+n-n_thisdir);
+            }
         }
     }
-
-    sfree(libpath);
+    GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
 
     if (n == 0 && bFatalError)
     {
index 53d26c6aa3e36f722e8d2eae1f997843ed116286..44d069d59d51ad15764b5948b2aa0d82339cff28 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2012,2013, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -189,9 +189,6 @@ gmx_string_hash_func(const char *s, unsigned int hash_init);
 /** Return value for gmx_wcmatch() when there is no match. */
 #define GMX_NO_WCMATCH 1
 
-/** Our implementation of strsep, the thread-safe replacement for strtok. */
-char *gmx_strsep(char **stringp, const char *delim);
-
 /*! \brief
  * Wraps lines, optionally indenting lines.
  *
index 0e619d6767a86def55ecde10cbb70edca79df701..de30f93d1e4482471ca5bfe5d91846cd90f976a2 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2011,2012,2013, by the GROMACS development team, led by
+ * Copyright (c) 2011,2012,2013,2014, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -41,6 +41,7 @@
  */
 #include "gromacs/utility/path.h"
 
+#include <cctype>
 #include <cerrno>
 #include <cstdlib>
 #include <cstring>
 #endif
 
 #include "gromacs/fileio/futil.h"
+#include "gromacs/utility/stringutil.h"
 
 namespace
 {
 
+//! \addtogroup module_utility
+//! \{
+
 //! Directory separator to use when joining paths.
 const char cDirSeparator = '/';
 //! Directory separators to use when parsing paths.
 const char cDirSeparators[] = "/\\";
+/*! \var cPathSeparator
+ * \brief
+ * Separator to use to split the PATH environment variable.
+ *
+ * When reading the PATH environment variable, Unix separates entries
+ * with colon, while windows uses semicolon.
+ */
+#ifdef GMX_NATIVE_WINDOWS
+const char cPathSeparator = ';';
+#else
+const char cPathSeparator = ':';
+#endif
 
 //! Check whether a given character is a directory separator.
 bool isDirSeparator(char chr)
@@ -74,6 +91,8 @@ bool isDirSeparator(char chr)
     return std::strchr(cDirSeparators, chr);
 }
 
+//! \}
+
 } // namespace
 
 namespace gmx
@@ -110,6 +129,11 @@ bool Path::isAbsolute(const std::string &path)
     return isAbsolute(path.c_str());
 }
 
+bool Path::startsWith(const std::string &path, const std::string &prefix)
+{
+    return gmx::startsWith(normalize(path), normalize(prefix));
+}
+
 std::string Path::join(const std::string &path1,
                        const std::string &path2)
 {
@@ -126,24 +150,40 @@ std::string Path::join(const std::string &path1,
     return path1 + cDirSeparator + path2 + cDirSeparator + path3;
 }
 
-std::pair<std::string, std::string>
-Path::splitToPathAndFilename(const std::string &path)
+std::string Path::getParentPath(const std::string &path)
 {
     size_t pos = path.find_last_of(cDirSeparators);
     if (pos == std::string::npos)
     {
-        return std::make_pair(std::string(), path);
+        return std::string();
     }
-    return std::make_pair(path.substr(0, pos), path.substr(pos+1));
+    return path.substr(0, pos);
+}
+
+std::string Path::getFilename(const std::string &path)
+{
+    size_t pos = path.find_last_of(cDirSeparators);
+    if (pos == std::string::npos)
+    {
+        return path;
+    }
+    return path.substr(pos+1);
 }
 
 std::string Path::normalize(const std::string &path)
 {
     std::string result(path);
+    // TODO: Remove . and .. entries.
     if (DIR_SEPARATOR != '/')
     {
         std::replace(result.begin(), result.end(), '/', DIR_SEPARATOR);
     }
+#ifdef GMX_NATIVE_WINDOWS
+    if (std::isalpha(result[0]) && result[1] == ':')
+    {
+        result[0] = std::toupper(result[0]);
+    }
+#endif
     return result;
 }
 
@@ -168,11 +208,11 @@ std::string Path::getWorkingDirectory()
 void Path::splitPathEnvironment(const std::string        &pathEnv,
                                 std::vector<std::string> *result)
 {
-    size_t                   prevPos = 0;
-    size_t                   separator;
+    size_t prevPos = 0;
+    size_t separator;
     do
     {
-        separator = pathEnv.find_first_of(PATH_SEPARATOR, prevPos);
+        separator = pathEnv.find(cPathSeparator, prevPos);
         result->push_back(pathEnv.substr(prevPos, separator - prevPos));
         prevPos = separator + 1;
     }
@@ -209,7 +249,7 @@ std::string Path::resolveSymlinks(const std::string &path)
         }
         else
         {
-            result = join(splitToPathAndFilename(result).first, buf);
+            result = join(getParentPath(result), buf);
         }
     }
 #endif
index 04e0255c066294dace134e8d51001a4e5303864c..a8132f7082de4c94f16b1bc5301fc4af559eecd8 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2011,2012,2013, by the GROMACS development team, led by
+ * Copyright (c) 2011,2012,2013,2014, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -56,15 +56,17 @@ class Path
         static bool containsDirectory(const std::string &path);
         static bool isAbsolute(const char *path);
         static bool isAbsolute(const std::string &path);
+        static bool startsWith(const std::string &path,
+                               const std::string &prefix);
 
         static std::string join(const std::string &path1,
                                 const std::string &path2);
         static std::string join(const std::string &path1,
                                 const std::string &path2,
                                 const std::string &path3);
-        static std::pair<std::string, std::string>
-        splitToPathAndFilename(const std::string &path);
         static std::string normalize(const std::string &path);
+        static std::string getParentPath(const std::string &path);
+        static std::string getFilename(const std::string &path);
 
         static bool exists(const char *path);
         static bool exists(const std::string &path);
index 6d850be384da737843d6d3810d6431cac81c73d1..5af68f142406b7c2209e67c79d913f120e72e14d 100644 (file)
@@ -68,6 +68,7 @@ class DefaultProgramContext : public ProgramContextInterface
         virtual const char *programName() const { return "GROMACS"; }
         virtual const char *displayName() const { return "GROMACS"; }
         virtual const char *fullBinaryPath() const { return ""; }
+        virtual const char *defaultLibraryDataPath() const { return ""; }
         virtual const char *commandLine() const { return ""; }
 };
 
index 72f2127965199abea5ff0874eaa73c1f3a9ec7f5..82d349a2a473c14823a833ce766263f20d3f2726 100644 (file)
@@ -97,13 +97,25 @@ class ProgramContextInterface
         /*! \brief
          * Returns the full path of the running binary.
          *
+         * This is mainly used for informational purposes.  There are no
+         * constraints on contents, except that it should not be `NULL`.
+         * Currently, this is also used for sanity checks in checkpointing.
+         *
          * The implementation can provide an empty string if the path to the
-         * binary is not available.  In such a case, some functionality in the
-         * library may be lost.
-         * Currently, this is used for locating \Gromacs data files, and for
-         * sanity checks in checkpointing.
+         * binary is not available.  In such a case, the information is not
+         * shown.
          */
         virtual const char *fullBinaryPath() const = 0;
+        /*! \brief
+         * Returns the default path for \Gromacs data files.
+         *
+         * This path is used to locate the data files that are in `share/top/`
+         * in the source directory.
+         * The implementation can provide an empty string if the path is not
+         * available; in such a case, functions that require data files may
+         * fail.
+         */
+        virtual const char *defaultLibraryDataPath() const = 0;
         /*! \brief
          * Returns the full command line used to invoke the binary.
          *