/*-------------------------------------------------------------------------
*
* 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);
/* 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().
#define symlink(oldpath, newpath) pgsymlink(oldpath, newpath)
#endif
-#endif
+#endif /* defined(WIN32) || defined(__CYGWIN__) */
extern bool rmtree(char *path, bool rmtopdir);
/* 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.
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))
*
*
* 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
*
* 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.
*/
#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)
{
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.
*
}
-
/*
- * 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,
}
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
-}
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
(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
}
+/*
+ * 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
*/
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;
{
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 */
}
-
/*
* get_etc_path
*/
}
-
/*
* get_include_path
*/
}
-
/*
* get_pkginclude_path
*/
}
-
/*
* get_includeserver_path
*/
}
-
/*
* get_lib_path
*/
}
-
/*
* get_pkglib_path
*/
}
-
/*
* get_locale_path
*
get_parent_directory(char *path)
{
trim_directory(path);
- trim_trailing_separator(path);
}
/*
* 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)
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);
}
/*
* 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';