]> granicus.if.org Git - postgresql/commitdiff
Code cleanup in path.c and exec.c. Handle Windows drive and network specs
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 6 Nov 2004 01:16:22 +0000 (01:16 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sat, 6 Nov 2004 01:16:22 +0000 (01:16 +0000)
everywhere not just some places, get rid of . and .. when joining path
sections together.  This should eliminate most of the ugly paths like
/foo/bar/./baz that we've been generating.

src/include/port.h
src/port/exec.c
src/port/path.c

index e6bde3de6e0b02687fc485c8c57fbb70e7e6cf07..c5dfee99779f5e40993a2a85c721cd4c3dc2f1c1 100644 (file)
@@ -1,12 +1,12 @@
 /*-------------------------------------------------------------------------
  *
  * port.h
- *       Header for /port compatibility functions.
+ *       Header for src/port/ compatibility functions.
  *
  * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/port.h,v 1.64 2004/10/11 22:50:33 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/port.h,v 1.65 2004/11/06 01:16:14 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include <ctype.h>
 
 /* non-blocking */
-bool           set_noblock(int sock);
+extern bool set_noblock(int sock);
 
 /* Portable path handling for Unix/Win32 */
 
-/* Find the location of the first directory separator, return
- * NULL if not found.
- */
 extern char *first_dir_separator(const char *filename);
-
-/* Find the location of the last directory separator, return
- * NULL if not found.
- */
 extern char *last_dir_separator(const char *filename);
-
-/* Find the location of the first path separator (i.e. ':' on
- * Unix, ';' on Windows), return NULL if not found.
- */
-extern char *first_path_separator(const char *filename);
-
+extern char *first_path_separator(const char *pathlist);
+extern void join_path_components(char *ret_path,
+                                                                const char *head, const char *tail);
 extern void canonicalize_path(char *path);
 extern void make_native_path(char *path);
 extern const char *get_progname(const char *argv0);
@@ -123,11 +113,6 @@ extern unsigned char pg_tolower(unsigned char ch);
 /* Portable prompt handling */
 extern char *simple_prompt(const char *prompt, int maxlen, bool echo);
 
-#if defined(bsdi) || defined(netbsd)
-extern int     fseeko(FILE *stream, off_t offset, int whence);
-extern off_t ftello(FILE *stream);
-#endif
-
 /*
  *     WIN32 doesn't allow descriptors returned by pipe() to be used in select(),
  *     so for that platform we use socket() instead of pipe().
@@ -185,7 +170,7 @@ extern int  pgsymlink(const char *oldpath, const char *newpath);
 #define symlink(oldpath, newpath)      pgsymlink(oldpath, newpath)
 #endif
 
-#endif
+#endif /* defined(WIN32) || defined(__CYGWIN__) */
 
 extern bool rmtree(char *path, bool rmtopdir);
 
@@ -212,14 +197,14 @@ extern void srand48(long seed);
 /* Last parameter not used */
 extern int     gettimeofday(struct timeval * tp, struct timezone * tzp);
 
-#else
+#else /* !WIN32 */
 
 /*
  *     Win32 requires a special close for sockets and pipes, while on Unix
  *     close() does them all.
  */
 #define closesocket close
-#endif
+#endif /* WIN32 */
 
 /*
  * Default "extern" declarations or macro substitutes for library routines.
@@ -229,6 +214,11 @@ extern int gettimeofday(struct timeval * tp, struct timezone * tzp);
 extern char *crypt(const char *key, const char *setting);
 #endif
 
+#if defined(bsdi) || defined(netbsd)
+extern int     fseeko(FILE *stream, off_t offset, int whence);
+extern off_t ftello(FILE *stream);
+#endif
+
 #ifndef HAVE_FSEEKO
 #define fseeko(a, b, c) fseek((a), (b), (c))
 #define ftello(a) ftell((a))
index 8d32754c8e7bd526e9343533de4fe40ba1e9c116..839bc73f00514859f02f13dcc9725a0e839fc64f 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/port/exec.c,v 1.30 2004/10/18 19:08:58 momjian Exp $
+ *       $PostgreSQL: pgsql/src/port/exec.c,v 1.31 2004/11/06 01:16:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 
 #ifndef FRONTEND
 /* We use only 3-parameter elog calls in this file, for simplicity */
-#define log_error(str, param)  elog(LOG, (str), (param))
+#define log_error(str, param)  elog(LOG, str, param)
 #else
-#define log_error(str, param)  fprintf(stderr, (str), (param))
+#define log_error(str, param)  (fprintf(stderr, str, param), fputc('\n', stderr))
 #endif
 
 
-static void win32_make_absolute(char *path);
-
-
 /*
  * validate_exec -- validate "path" as an executable file
  *
@@ -165,7 +162,7 @@ validate_exec(const char *path)
  * executable's location.  Also, we need a full path not a relative
  * path because we will later change working directory.
  *
- * This function is not thread-safe because of it calls validate_exec(),
+ * This function is not thread-safe because it calls validate_exec(),
  * which calls getgrgid().     This function should be used only in
  * non-threaded binaries, not in library routines.
  */
@@ -178,61 +175,40 @@ find_my_exec(const char *argv0, char *retpath)
 
 #ifndef WIN32_CLIENT_ONLY
        if (!getcwd(cwd, MAXPGPATH))
+               strcpy(cwd, ".");               /* cheesy, but better than nothing */
 #else
        if (!GetCurrentDirectory(MAXPGPATH, cwd))
+               strcpy(cwd, ".");               /* cheesy, but better than nothing */
 #endif
-               cwd[0] = '\0';
 
        /*
-        * First try: use the binary that's located in the same directory if
-        * it was invoked with an explicit path. Presumably the user used an
-        * explicit path because it wasn't in PATH, and we don't want to use
-        * incompatible executables.
-        *
-        * For the binary: First try: if we're given some kind of path, use it
-        * (making sure that a relative path is made absolute before returning
-        * it).
+        * If argv0 contains a separator, then PATH wasn't used.
         */
-       /* Does argv0 have a separator? */
-       if ((path = last_dir_separator(argv0)))
+       if (first_dir_separator(argv0) != NULL)
        {
-               if (*++path == '\0')
-               {
-                       log_error("argv[0] ends with a path separator \"%s\"", argv0);
-                       return -1;
-               }
-
                if (is_absolute_path(argv0))
                        StrNCpy(retpath, argv0, MAXPGPATH);
                else
-                       snprintf(retpath, MAXPGPATH, "%s/%s", cwd, argv0);
-
+                       join_path_components(retpath, cwd, argv0);
                canonicalize_path(retpath);
+
                if (validate_exec(retpath) == 0)
-               {
-                       win32_make_absolute(retpath);
                        return 0;
-               }
-               else
-               {
-                       log_error("invalid binary \"%s\"", retpath);
-                       return -1;
-               }
+
+               log_error("invalid binary \"%s\"", retpath);
+               return -1;
        }
 
 #ifdef WIN32
        /* Win32 checks the current directory first for names without slashes */
-       if (validate_exec(argv0) == 0)
-       {
-               snprintf(retpath, MAXPGPATH, "%s/%s", cwd, argv0);
-               win32_make_absolute(retpath);
+       join_path_components(retpath, cwd, argv0);
+       if (validate_exec(retpath) == 0)
                return 0;
-       }
 #endif
 
        /*
-        * Second try: since no explicit path was supplied, the user must have
-        * been relying on PATH.  We'll use the same PATH.
+        * Since no explicit path was supplied, the user must have
+        * been relying on PATH.  We'll search the same PATH.
         */
        if ((path = getenv("PATH")) && *path)
        {
@@ -253,40 +229,33 @@ find_my_exec(const char *argv0, char *retpath)
                        StrNCpy(test_path, startp, Min(endp - startp + 1, MAXPGPATH));
 
                        if (is_absolute_path(test_path))
-                               snprintf(retpath, MAXPGPATH, "%s/%s", test_path, argv0);
+                               join_path_components(retpath, test_path, argv0);
                        else
-                               snprintf(retpath, MAXPGPATH, "%s/%s/%s", cwd, test_path, argv0);
-
+                       {
+                               join_path_components(retpath, cwd, test_path);
+                               join_path_components(retpath, retpath, argv0);
+                       }
                        canonicalize_path(retpath);
+
                        switch (validate_exec(retpath))
                        {
-                               case 0: /* found ok */
-                                       win32_make_absolute(retpath);
+                               case 0:                 /* found ok */
                                        return 0;
                                case -1:                /* wasn't even a candidate, keep looking */
-                                       continue;
+                                       break;
                                case -2:                /* found but disqualified */
                                        log_error("could not read binary \"%s\"", retpath);
-                                       continue;
+                                       break;
                        }
                } while (*endp);
        }
 
        log_error("could not find a \"%s\" to execute", argv0);
        return -1;
-
-#if NOT_USED
-       /*
-        * Win32 has a native way to find the executable name, but the above
-        * method works too.
-        */
-       if (GetModuleFileName(NULL, retpath, MAXPGPATH) == 0)
-               log_error("GetModuleFileName failed (%i)", (int) GetLastError());
-#endif
 }
 
 /*
- * The runtime librarys popen() on win32 does not work when being
+ * The runtime library's popen() on win32 does not work when being
  * called from a service when running on windows <= 2000, because
  * there is no stdin/stdout/stderr.
  *
@@ -427,10 +396,9 @@ pipe_read_line(char *cmd, char *line, int maxsize)
 }
 
 
-
 /*
- * Find our binary directory, then make sure the "target" executable
- * is the proper version.
+ * Find another program in our binary's directory,
+ * then make sure it is the proper version.
  */
 int
 find_other_exec(const char *argv0, const char *target,
@@ -487,41 +455,19 @@ pclose_check(FILE *stream)
        }
        else if (WIFEXITED(exitstatus))
        {
-               log_error(_("child process exited with exit code %d\n"),
+               log_error(_("child process exited with exit code %d"),
                                  WEXITSTATUS(exitstatus));
        }
        else if (WIFSIGNALED(exitstatus))
        {
-               log_error(_("child process was terminated by signal %d\n"),
+               log_error(_("child process was terminated by signal %d"),
                                  WTERMSIG(exitstatus));
        }
        else
        {
-               log_error(_("child process exited with unrecognized status %d\n"),
+               log_error(_("child process exited with unrecognized status %d"),
                                  exitstatus);
        }
 
        return -1;
 }
-
-
-/*
- * Windows doesn't like relative paths to executables (other things work fine)
- * so we call its builtin function to expand them. Elsewhere this is a NOOP
- */
-static void
-win32_make_absolute(char *path)
-{
-#ifdef WIN32
-       char            abspath[MAXPGPATH];
-
-       if (_fullpath(abspath, path, MAXPGPATH) == NULL)
-       {
-               log_error("Win32 path expansion failed: %s", strerror(errno));
-               StrNCpy(abspath, path, MAXPGPATH);
-       }
-       canonicalize_path(abspath);
-
-       StrNCpy(path, abspath, MAXPGPATH);
-#endif
-}
index 5d94227106fe2b9902bf82fe1c148f2f09e965bc..1c630043ab66d910a13471c0947e09b03f655bec 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/port/path.c,v 1.41 2004/11/02 03:09:06 momjian Exp $
+ *       $PostgreSQL: pgsql/src/port/path.c,v 1.42 2004/11/06 01:16:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -44,53 +44,95 @@ static void trim_trailing_separator(char *path);
                (p)++; \
 }
 
+/*
+ * skip_drive
+ *
+ * On Windows, a path may begin with "C:" or "//network/".  Advance over
+ * this and point to the effective start of the path.
+ */
+#ifdef WIN32
+
+static char *
+skip_drive(const char *path)
+{
+       if (IS_DIR_SEP(path[0]) && IS_DIR_SEP(path[1]))
+       {
+               path += 2;
+               while (*path && !IS_DIR_SEP(*path))
+                       path++;
+       }
+       else if (isalpha(path[0]) && path[1] == ':')
+       {
+               path += 2;
+       }
+       return (char *) path;
+}
+
+#else
+
+#define skip_drive(path)       (path)
+
+#endif
+
 /*
  *     first_dir_separator
+ *
+ * Find the location of the first directory separator, return
+ * NULL if not found.
  */
 char *
 first_dir_separator(const char *filename)
 {
-       char       *p;
+       const char *p;
 
-       for (p = (char *) filename; *p; p++)
+       for (p = skip_drive(filename); *p; p++)
                if (IS_DIR_SEP(*p))
-                       return p;
+                       return (char *) p;
        return NULL;
 }
 
 /*
  *     first_path_separator
+ *
+ * Find the location of the first path separator (i.e. ':' on
+ * Unix, ';' on Windows), return NULL if not found.
  */
 char *
-first_path_separator(const char *filename)
+first_path_separator(const char *pathlist)
 {
-       char       *p;
+       const char *p;
 
-       for (p = (char *) filename; *p; p++)
+       /* skip_drive is not needed */
+       for (p = pathlist; *p; p++)
                if (IS_PATH_SEP(*p))
-                       return p;
+                       return (char *) p;
        return NULL;
 }
 
 /*
  *     last_dir_separator
+ *
+ * Find the location of the last directory separator, return
+ * NULL if not found.
  */
 char *
 last_dir_separator(const char *filename)
 {
-       char       *p,
+       const char *p,
                           *ret = NULL;
 
-       for (p = (char *) filename; *p; p++)
+       for (p = skip_drive(filename); *p; p++)
                if (IS_DIR_SEP(*p))
                        ret = p;
-       return ret;
+       return (char *) ret;
 }
 
 
 /*
  *     make_native_path - on WIN32, change / to \ in the path
  *
+ *     This effectively undoes canonicalize_path.
+ *
  *     This is required because WIN32 COPY is an internal CMD.EXE
  *     command and doesn't process forward slashes in the same way
  *     as external commands.  Quoting the first argument to COPY
@@ -114,11 +156,59 @@ make_native_path(char *filename)
 }
 
 
+/*
+ * join_path_components - join two path components, inserting a slash
+ *
+ * ret_path is the output area (must be of size MAXPGPATH)
+ *
+ * ret_path can be the same as head, but not the same as tail.
+ */
+void
+join_path_components(char *ret_path,
+                                        const char *head, const char *tail)
+{
+       if (ret_path != head)
+               StrNCpy(ret_path, head, MAXPGPATH);
+       /*
+        * Remove any leading "." and ".." in the tail component,
+        * adjusting head as needed.
+        */
+       for (;;)
+       {
+               if (tail[0] == '.' && IS_DIR_SEP(tail[1]))
+               {
+                       tail += 2;
+               }
+               else if (tail[0] == '.' && tail[1] == '\0')
+               {
+                       tail += 1;
+                       break;
+               }
+               else if (tail[0] == '.' && tail[1] == '.' && IS_DIR_SEP(tail[2]))
+               {
+                       trim_directory(ret_path);
+                       tail += 3;
+               }
+               else if (tail[0] == '.' && tail[1] == '.' && tail[2] == '\0')
+               {
+                       trim_directory(ret_path);
+                       tail += 2;
+                       break;
+               }
+               else
+                       break;
+       }
+       if (*tail)
+               snprintf(ret_path + strlen(ret_path), MAXPGPATH - strlen(ret_path),
+                                "/%s", tail);
+}
+
+
 /*
  *     Clean up path by:
  *             o  make Win32 path use Unix slashes
- *             o  remove trailling quote on Win32
- *             o  remove trailling slash
+ *             o  remove trailing quote on Win32
+ *             o  remove trailing slash
  *             o  remove trailing '.'
  *             o  process trailing '..' ourselves
  */
@@ -165,13 +255,11 @@ canonicalize_path(char *path)
                if (len >= 2 && strcmp(path + len - 2, "/.") == 0)
                {
                        trim_directory(path);
-                       trim_trailing_separator(path);
                }
                else if (len >= 3 && strcmp(path + len - 3, "/..") == 0)
                {
                        trim_directory(path);
                        trim_directory(path);   /* remove directory above */
-                       trim_trailing_separator(path);
                }
                else
                        break;
@@ -188,10 +276,11 @@ get_progname(const char *argv0)
 {
        const char *nodir_name;
 
-       if (!last_dir_separator(argv0))
-               nodir_name = argv0;
+       nodir_name = last_dir_separator(argv0);
+       if (nodir_name)
+               nodir_name++;
        else
-               nodir_name = last_dir_separator(argv0) + 1;
+               nodir_name = skip_drive(argv0);
 
 #if defined(__CYGWIN__) || defined(WIN32)
        /* strip .exe suffix, regardless of case */
@@ -231,7 +320,6 @@ get_share_path(const char *my_exec_path, char *ret_path)
 }
 
 
-
 /*
  *     get_etc_path
  */
@@ -248,7 +336,6 @@ get_etc_path(const char *my_exec_path, char *ret_path)
 }
 
 
-
 /*
  *     get_include_path
  */
@@ -265,7 +352,6 @@ get_include_path(const char *my_exec_path, char *ret_path)
 }
 
 
-
 /*
  *     get_pkginclude_path
  */
@@ -282,7 +368,6 @@ get_pkginclude_path(const char *my_exec_path, char *ret_path)
 }
 
 
-
 /*
  *     get_includeserver_path
  */
@@ -299,7 +384,6 @@ get_includeserver_path(const char *my_exec_path, char *ret_path)
 }
 
 
-
 /*
  *     get_lib_path
  */
@@ -316,7 +400,6 @@ get_lib_path(const char *my_exec_path, char *ret_path)
 }
 
 
-
 /*
  *     get_pkglib_path
  */
@@ -333,7 +416,6 @@ get_pkglib_path(const char *my_exec_path, char *ret_path)
 }
 
 
-
 /*
  *     get_locale_path
  *
@@ -382,7 +464,6 @@ void
 get_parent_directory(char *path)
 {
        trim_directory(path);
-       trim_trailing_separator(path);
 }
 
 
@@ -436,6 +517,8 @@ set_pglocale_pgservice(const char *argv0, const char *app)
 
 /*
  *     make_relative - adjust path to be relative to bin/
+ *
+ * ret_path is the output area (must be of size MAXPGPATH)
  */
 static void
 make_relative(const char *my_exec_path, const char *p, char *ret_path)
@@ -443,9 +526,9 @@ make_relative(const char *my_exec_path, const char *p, char *ret_path)
        char            path[MAXPGPATH];
 
        StrNCpy(path, my_exec_path, MAXPGPATH);
-       trim_directory(path);
-       trim_directory(path);
-       snprintf(ret_path, MAXPGPATH, "%s/%s", path, p);
+       trim_directory(path);           /* remove my executable name */
+       trim_directory(path);           /* remove last directory component (/bin) */
+       join_path_components(ret_path, path, p);
 }
 
 
@@ -520,57 +603,48 @@ relative_path(const char *bin_path, const char *other_path)
 /*
  *     trim_directory
  *
- *     Trim trailing directory from path
+ *     Trim trailing directory from path, that is, remove any trailing slashes,
+ *     the last pathname component, and the slash just ahead of it --- but never
+ *     remove a leading slash.
  */
 static void
 trim_directory(char *path)
 {
        char       *p;
 
+       path = skip_drive(path);
+
        if (path[0] == '\0')
                return;
 
+       /* back up over trailing slash(es) */
        for (p = path + strlen(path) - 1; IS_DIR_SEP(*p) && p > path; p--)
                ;
+       /* back up over directory name */
        for (; !IS_DIR_SEP(*p) && p > path; p--)
                ;
+       /* if multiple slashes before directory name, remove 'em all */
+       for (; p > path && IS_DIR_SEP(*(p - 1)); p--)
+               ;
+       /* don't erase a leading slash */
+       if (p == path && IS_DIR_SEP(*p))
+               p++;
        *p = '\0';
 }
 
 
-
 /*
  *     trim_trailing_separator
+ *
+ * trim off trailing slashes, but not a leading slash
  */
 static void
 trim_trailing_separator(char *path)
 {
-       char       *p = path + strlen(path);
-
-#ifdef WIN32
-
-       /*
-        * Skip over network and drive specifiers for win32. Set 'path' to
-        * point to the last character we must keep.
-        */
-       if (strlen(path) >= 2)
-       {
-               if (IS_DIR_SEP(path[0]) && IS_DIR_SEP(path[1]))
-               {
-                       path += 2;
-                       while (*path && !IS_DIR_SEP(*path))
-                               path++;
-               }
-               else if (isalpha(path[0]) && path[1] == ':')
-               {
-                       path++;
-                       if (IS_DIR_SEP(path[1]))
-                               path++;
-               }
-       }
-#endif
+       char       *p;
 
-       /* trim off trailing slashes */
+       path = skip_drive(path);
+       p = path + strlen(path);
        if (p > path)
                for (p--; p > path && IS_DIR_SEP(*p); p--)
                        *p = '\0';