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';