Commit 4867afef authored by Tom Lane's avatar Tom Lane

Code cleanup in path.c and exec.c. Handle Windows drive and network specs

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.
parent 3d6e538e
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* port.h * 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) 1996-2004, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -20,25 +20,15 @@ ...@@ -20,25 +20,15 @@
#include <ctype.h> #include <ctype.h>
/* non-blocking */ /* non-blocking */
bool set_noblock(int sock); extern bool set_noblock(int sock);
/* Portable path handling for Unix/Win32 */ /* 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); 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); extern char *last_dir_separator(const char *filename);
extern char *first_path_separator(const char *pathlist);
/* Find the location of the first path separator (i.e. ':' on extern void join_path_components(char *ret_path,
* Unix, ';' on Windows), return NULL if not found. const char *head, const char *tail);
*/
extern char *first_path_separator(const char *filename);
extern void canonicalize_path(char *path); extern void canonicalize_path(char *path);
extern void make_native_path(char *path); extern void make_native_path(char *path);
extern const char *get_progname(const char *argv0); extern const char *get_progname(const char *argv0);
...@@ -123,11 +113,6 @@ extern unsigned char pg_tolower(unsigned char ch); ...@@ -123,11 +113,6 @@ extern unsigned char pg_tolower(unsigned char ch);
/* Portable prompt handling */ /* Portable prompt handling */
extern char *simple_prompt(const char *prompt, int maxlen, bool echo); 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(), * WIN32 doesn't allow descriptors returned by pipe() to be used in select(),
* so for that platform we use socket() instead of pipe(). * so for that platform we use socket() instead of pipe().
...@@ -185,7 +170,7 @@ extern int pgsymlink(const char *oldpath, const char *newpath); ...@@ -185,7 +170,7 @@ extern int pgsymlink(const char *oldpath, const char *newpath);
#define symlink(oldpath, newpath) pgsymlink(oldpath, newpath) #define symlink(oldpath, newpath) pgsymlink(oldpath, newpath)
#endif #endif
#endif #endif /* defined(WIN32) || defined(__CYGWIN__) */
extern bool rmtree(char *path, bool rmtopdir); extern bool rmtree(char *path, bool rmtopdir);
...@@ -212,14 +197,14 @@ extern void srand48(long seed); ...@@ -212,14 +197,14 @@ extern void srand48(long seed);
/* Last parameter not used */ /* Last parameter not used */
extern int gettimeofday(struct timeval * tp, struct timezone * tzp); extern int gettimeofday(struct timeval * tp, struct timezone * tzp);
#else #else /* !WIN32 */
/* /*
* Win32 requires a special close for sockets and pipes, while on Unix * Win32 requires a special close for sockets and pipes, while on Unix
* close() does them all. * close() does them all.
*/ */
#define closesocket close #define closesocket close
#endif #endif /* WIN32 */
/* /*
* Default "extern" declarations or macro substitutes for library routines. * Default "extern" declarations or macro substitutes for library routines.
...@@ -229,6 +214,11 @@ extern int gettimeofday(struct timeval * tp, struct timezone * tzp); ...@@ -229,6 +214,11 @@ extern int gettimeofday(struct timeval * tp, struct timezone * tzp);
extern char *crypt(const char *key, const char *setting); extern char *crypt(const char *key, const char *setting);
#endif #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 #ifndef HAVE_FSEEKO
#define fseeko(a, b, c) fseek((a), (b), (c)) #define fseeko(a, b, c) fseek((a), (b), (c))
#define ftello(a) ftell((a)) #define ftello(a) ftell((a))
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * 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 $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -42,15 +42,12 @@ ...@@ -42,15 +42,12 @@
#ifndef FRONTEND #ifndef FRONTEND
/* We use only 3-parameter elog calls in this file, for simplicity */ /* 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 #else
#define log_error(str, param) fprintf(stderr, (str), (param)) #define log_error(str, param) (fprintf(stderr, str, param), fputc('\n', stderr))
#endif #endif
static void win32_make_absolute(char *path);
/* /*
* validate_exec -- validate "path" as an executable file * validate_exec -- validate "path" as an executable file
* *
...@@ -165,7 +162,7 @@ validate_exec(const char *path) ...@@ -165,7 +162,7 @@ validate_exec(const char *path)
* executable's location. Also, we need a full path not a relative * executable's location. Also, we need a full path not a relative
* path because we will later change working directory. * 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 * which calls getgrgid(). This function should be used only in
* non-threaded binaries, not in library routines. * non-threaded binaries, not in library routines.
*/ */
...@@ -178,61 +175,40 @@ find_my_exec(const char *argv0, char *retpath) ...@@ -178,61 +175,40 @@ find_my_exec(const char *argv0, char *retpath)
#ifndef WIN32_CLIENT_ONLY #ifndef WIN32_CLIENT_ONLY
if (!getcwd(cwd, MAXPGPATH)) if (!getcwd(cwd, MAXPGPATH))
strcpy(cwd, "."); /* cheesy, but better than nothing */
#else #else
if (!GetCurrentDirectory(MAXPGPATH, cwd)) if (!GetCurrentDirectory(MAXPGPATH, cwd))
strcpy(cwd, "."); /* cheesy, but better than nothing */
#endif #endif
cwd[0] = '\0';
/* /*
* First try: use the binary that's located in the same directory if * If argv0 contains a separator, then PATH wasn't used.
* 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).
*/ */
/* Does argv0 have a separator? */ if (first_dir_separator(argv0) != NULL)
if ((path = last_dir_separator(argv0)))
{ {
if (*++path == '\0')
{
log_error("argv[0] ends with a path separator \"%s\"", argv0);
return -1;
}
if (is_absolute_path(argv0)) if (is_absolute_path(argv0))
StrNCpy(retpath, argv0, MAXPGPATH); StrNCpy(retpath, argv0, MAXPGPATH);
else else
snprintf(retpath, MAXPGPATH, "%s/%s", cwd, argv0); join_path_components(retpath, cwd, argv0);
canonicalize_path(retpath); canonicalize_path(retpath);
if (validate_exec(retpath) == 0) if (validate_exec(retpath) == 0)
{
win32_make_absolute(retpath);
return 0; return 0;
}
else log_error("invalid binary \"%s\"", retpath);
{ return -1;
log_error("invalid binary \"%s\"", retpath);
return -1;
}
} }
#ifdef WIN32 #ifdef WIN32
/* Win32 checks the current directory first for names without slashes */ /* Win32 checks the current directory first for names without slashes */
if (validate_exec(argv0) == 0) join_path_components(retpath, cwd, argv0);
{ if (validate_exec(retpath) == 0)
snprintf(retpath, MAXPGPATH, "%s/%s", cwd, argv0);
win32_make_absolute(retpath);
return 0; return 0;
}
#endif #endif
/* /*
* Second try: since no explicit path was supplied, the user must have * Since no explicit path was supplied, the user must have
* been relying on PATH. We'll use the same PATH. * been relying on PATH. We'll search the same PATH.
*/ */
if ((path = getenv("PATH")) && *path) if ((path = getenv("PATH")) && *path)
{ {
...@@ -253,40 +229,33 @@ find_my_exec(const char *argv0, char *retpath) ...@@ -253,40 +229,33 @@ find_my_exec(const char *argv0, char *retpath)
StrNCpy(test_path, startp, Min(endp - startp + 1, MAXPGPATH)); StrNCpy(test_path, startp, Min(endp - startp + 1, MAXPGPATH));
if (is_absolute_path(test_path)) if (is_absolute_path(test_path))
snprintf(retpath, MAXPGPATH, "%s/%s", test_path, argv0); join_path_components(retpath, test_path, argv0);
else 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); canonicalize_path(retpath);
switch (validate_exec(retpath)) switch (validate_exec(retpath))
{ {
case 0: /* found ok */ case 0: /* found ok */
win32_make_absolute(retpath);
return 0; return 0;
case -1: /* wasn't even a candidate, keep looking */ case -1: /* wasn't even a candidate, keep looking */
continue; break;
case -2: /* found but disqualified */ case -2: /* found but disqualified */
log_error("could not read binary \"%s\"", retpath); log_error("could not read binary \"%s\"", retpath);
continue; break;
} }
} while (*endp); } while (*endp);
} }
log_error("could not find a \"%s\" to execute", argv0); log_error("could not find a \"%s\" to execute", argv0);
return -1; 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 * called from a service when running on windows <= 2000, because
* there is no stdin/stdout/stderr. * there is no stdin/stdout/stderr.
* *
...@@ -427,10 +396,9 @@ pipe_read_line(char *cmd, char *line, int maxsize) ...@@ -427,10 +396,9 @@ pipe_read_line(char *cmd, char *line, int maxsize)
} }
/* /*
* Find our binary directory, then make sure the "target" executable * Find another program in our binary's directory,
* is the proper version. * then make sure it is the proper version.
*/ */
int int
find_other_exec(const char *argv0, const char *target, find_other_exec(const char *argv0, const char *target,
...@@ -487,41 +455,19 @@ pclose_check(FILE *stream) ...@@ -487,41 +455,19 @@ pclose_check(FILE *stream)
} }
else if (WIFEXITED(exitstatus)) 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)); WEXITSTATUS(exitstatus));
} }
else if (WIFSIGNALED(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)); WTERMSIG(exitstatus));
} }
else else
{ {
log_error(_("child process exited with unrecognized status %d\n"), log_error(_("child process exited with unrecognized status %d"),
exitstatus); exitstatus);
} }
return -1; 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
}
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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); ...@@ -44,53 +44,95 @@ static void trim_trailing_separator(char *path);
(p)++; \ (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 * first_dir_separator
*
* Find the location of the first directory separator, return
* NULL if not found.
*/ */
char * char *
first_dir_separator(const char *filename) 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)) if (IS_DIR_SEP(*p))
return p; return (char *) p;
return NULL; return NULL;
} }
/* /*
* first_path_separator * first_path_separator
*
* Find the location of the first path separator (i.e. ':' on
* Unix, ';' on Windows), return NULL if not found.
*/ */
char * 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)) if (IS_PATH_SEP(*p))
return p; return (char *) p;
return NULL; return NULL;
} }
/* /*
* last_dir_separator * last_dir_separator
*
* Find the location of the last directory separator, return
* NULL if not found.
*/ */
char * char *
last_dir_separator(const char *filename) last_dir_separator(const char *filename)
{ {
char *p, const char *p,
*ret = NULL; *ret = NULL;
for (p = (char *) filename; *p; p++) for (p = skip_drive(filename); *p; p++)
if (IS_DIR_SEP(*p)) if (IS_DIR_SEP(*p))
ret = p; ret = p;
return ret; return (char *) ret;
} }
/* /*
* make_native_path - on WIN32, change / to \ in the path * 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 * This is required because WIN32 COPY is an internal CMD.EXE
* command and doesn't process forward slashes in the same way * command and doesn't process forward slashes in the same way
* as external commands. Quoting the first argument to COPY * as external commands. Quoting the first argument to COPY
...@@ -114,11 +156,59 @@ make_native_path(char *filename) ...@@ -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: * Clean up path by:
* o make Win32 path use Unix slashes * o make Win32 path use Unix slashes
* o remove trailling quote on Win32 * o remove trailing quote on Win32
* o remove trailling slash * o remove trailing slash
* o remove trailing '.' * o remove trailing '.'
* o process trailing '..' ourselves * o process trailing '..' ourselves
*/ */
...@@ -165,13 +255,11 @@ canonicalize_path(char *path) ...@@ -165,13 +255,11 @@ canonicalize_path(char *path)
if (len >= 2 && strcmp(path + len - 2, "/.") == 0) if (len >= 2 && strcmp(path + len - 2, "/.") == 0)
{ {
trim_directory(path); trim_directory(path);
trim_trailing_separator(path);
} }
else if (len >= 3 && strcmp(path + len - 3, "/..") == 0) else if (len >= 3 && strcmp(path + len - 3, "/..") == 0)
{ {
trim_directory(path); trim_directory(path);
trim_directory(path); /* remove directory above */ trim_directory(path); /* remove directory above */
trim_trailing_separator(path);
} }
else else
break; break;
...@@ -188,10 +276,11 @@ get_progname(const char *argv0) ...@@ -188,10 +276,11 @@ get_progname(const char *argv0)
{ {
const char *nodir_name; const char *nodir_name;
if (!last_dir_separator(argv0)) nodir_name = last_dir_separator(argv0);
nodir_name = argv0; if (nodir_name)
nodir_name++;
else else
nodir_name = last_dir_separator(argv0) + 1; nodir_name = skip_drive(argv0);
#if defined(__CYGWIN__) || defined(WIN32) #if defined(__CYGWIN__) || defined(WIN32)
/* strip .exe suffix, regardless of case */ /* strip .exe suffix, regardless of case */
...@@ -231,7 +320,6 @@ get_share_path(const char *my_exec_path, char *ret_path) ...@@ -231,7 +320,6 @@ get_share_path(const char *my_exec_path, char *ret_path)
} }
/* /*
* get_etc_path * get_etc_path
*/ */
...@@ -248,7 +336,6 @@ get_etc_path(const char *my_exec_path, char *ret_path) ...@@ -248,7 +336,6 @@ get_etc_path(const char *my_exec_path, char *ret_path)
} }
/* /*
* get_include_path * get_include_path
*/ */
...@@ -265,7 +352,6 @@ get_include_path(const char *my_exec_path, char *ret_path) ...@@ -265,7 +352,6 @@ get_include_path(const char *my_exec_path, char *ret_path)
} }
/* /*
* get_pkginclude_path * get_pkginclude_path
*/ */
...@@ -282,7 +368,6 @@ get_pkginclude_path(const char *my_exec_path, char *ret_path) ...@@ -282,7 +368,6 @@ get_pkginclude_path(const char *my_exec_path, char *ret_path)
} }
/* /*
* get_includeserver_path * get_includeserver_path
*/ */
...@@ -299,7 +384,6 @@ get_includeserver_path(const char *my_exec_path, char *ret_path) ...@@ -299,7 +384,6 @@ get_includeserver_path(const char *my_exec_path, char *ret_path)
} }
/* /*
* get_lib_path * get_lib_path
*/ */
...@@ -316,7 +400,6 @@ get_lib_path(const char *my_exec_path, char *ret_path) ...@@ -316,7 +400,6 @@ get_lib_path(const char *my_exec_path, char *ret_path)
} }
/* /*
* get_pkglib_path * get_pkglib_path
*/ */
...@@ -333,7 +416,6 @@ get_pkglib_path(const char *my_exec_path, char *ret_path) ...@@ -333,7 +416,6 @@ get_pkglib_path(const char *my_exec_path, char *ret_path)
} }
/* /*
* get_locale_path * get_locale_path
* *
...@@ -382,7 +464,6 @@ void ...@@ -382,7 +464,6 @@ void
get_parent_directory(char *path) get_parent_directory(char *path)
{ {
trim_directory(path); trim_directory(path);
trim_trailing_separator(path);
} }
...@@ -436,6 +517,8 @@ set_pglocale_pgservice(const char *argv0, const char *app) ...@@ -436,6 +517,8 @@ set_pglocale_pgservice(const char *argv0, const char *app)
/* /*
* make_relative - adjust path to be relative to bin/ * make_relative - adjust path to be relative to bin/
*
* ret_path is the output area (must be of size MAXPGPATH)
*/ */
static void static void
make_relative(const char *my_exec_path, const char *p, char *ret_path) 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) ...@@ -443,9 +526,9 @@ make_relative(const char *my_exec_path, const char *p, char *ret_path)
char path[MAXPGPATH]; char path[MAXPGPATH];
StrNCpy(path, my_exec_path, MAXPGPATH); StrNCpy(path, my_exec_path, MAXPGPATH);
trim_directory(path); trim_directory(path); /* remove my executable name */
trim_directory(path); trim_directory(path); /* remove last directory component (/bin) */
snprintf(ret_path, MAXPGPATH, "%s/%s", path, p); join_path_components(ret_path, path, p);
} }
...@@ -520,57 +603,48 @@ relative_path(const char *bin_path, const char *other_path) ...@@ -520,57 +603,48 @@ relative_path(const char *bin_path, const char *other_path)
/* /*
* trim_directory * 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 static void
trim_directory(char *path) trim_directory(char *path)
{ {
char *p; char *p;
path = skip_drive(path);
if (path[0] == '\0') if (path[0] == '\0')
return; return;
/* back up over trailing slash(es) */
for (p = path + strlen(path) - 1; IS_DIR_SEP(*p) && p > path; p--) 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--) 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'; *p = '\0';
} }
/* /*
* trim_trailing_separator * trim_trailing_separator
*
* trim off trailing slashes, but not a leading slash
*/ */
static void static void
trim_trailing_separator(char *path) trim_trailing_separator(char *path)
{ {
char *p = path + strlen(path); char *p;
#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
/* trim off trailing slashes */ path = skip_drive(path);
p = path + strlen(path);
if (p > path) if (p > path)
for (p--; p > path && IS_DIR_SEP(*p); p--) for (p--; p > path && IS_DIR_SEP(*p); p--)
*p = '\0'; *p = '\0';
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment