1
/*-------------------------------------------------------------------------
4
* portable path handling routines
6
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
7
* Portions Copyright (c) 1994, Regents of the University of California
11
* $PostgreSQL: pgsql/src/port/path.c,v 1.50.4.1 2005-01-26 19:24:21 tgl Exp $
13
*-------------------------------------------------------------------------
24
#define _WIN32_IE 0x0500
34
#include "pg_config_paths.h"
38
#define IS_DIR_SEP(ch) ((ch) == '/')
40
#define IS_DIR_SEP(ch) ((ch) == '/' || (ch) == '\\')
44
#define IS_PATH_SEP(ch) ((ch) == ':')
46
#define IS_PATH_SEP(ch) ((ch) == ';')
49
static void make_relative_path(char *ret_path, const char *target_path,
50
const char *bin_path, const char *my_exec_path);
51
static void trim_directory(char *path);
52
static void trim_trailing_separator(char *path);
58
* On Windows, a path may begin with "C:" or "//network/". Advance over
59
* this and point to the effective start of the path.
64
skip_drive(const char *path)
66
if (IS_DIR_SEP(path[0]) && IS_DIR_SEP(path[1]))
69
while (*path && !IS_DIR_SEP(*path))
72
else if (isalpha(path[0]) && path[1] == ':')
81
#define skip_drive(path) (path)
88
* Find the location of the first directory separator, return
92
first_dir_separator(const char *filename)
96
for (p = skip_drive(filename); *p; p++)
103
* first_path_separator
105
* Find the location of the first path separator (i.e. ':' on
106
* Unix, ';' on Windows), return NULL if not found.
109
first_path_separator(const char *pathlist)
113
/* skip_drive is not needed */
114
for (p = pathlist; *p; p++)
123
* Find the location of the last directory separator, return
127
last_dir_separator(const char *filename)
132
for (p = skip_drive(filename); *p; p++)
140
* make_native_path - on WIN32, change / to \ in the path
142
* This effectively undoes canonicalize_path.
144
* This is required because WIN32 COPY is an internal CMD.EXE
145
* command and doesn't process forward slashes in the same way
146
* as external commands. Quoting the first argument to COPY
147
* does not convert forward to backward slashes, but COPY does
148
* properly process quoted forward slashes in the second argument.
150
* COPY works with quoted forward slashes in the first argument
151
* only if the current directory is the same as the directory
152
* of the first argument.
155
make_native_path(char *filename)
160
for (p = filename; *p; p++)
168
* join_path_components - join two path components, inserting a slash
170
* ret_path is the output area (must be of size MAXPGPATH)
172
* ret_path can be the same as head, but not the same as tail.
175
join_path_components(char *ret_path,
176
const char *head, const char *tail)
178
if (ret_path != head)
179
StrNCpy(ret_path, head, MAXPGPATH);
181
* Remove any leading "." and ".." in the tail component,
182
* adjusting head as needed.
186
if (tail[0] == '.' && IS_DIR_SEP(tail[1]))
190
else if (tail[0] == '.' && tail[1] == '\0')
195
else if (tail[0] == '.' && tail[1] == '.' && IS_DIR_SEP(tail[2]))
197
trim_directory(ret_path);
200
else if (tail[0] == '.' && tail[1] == '.' && tail[2] == '\0')
202
trim_directory(ret_path);
210
snprintf(ret_path + strlen(ret_path), MAXPGPATH - strlen(ret_path),
217
* o make Win32 path use Unix slashes
218
* o remove trailing quote on Win32
219
* o remove trailing slash
220
* o remove duplicate adjacent separators
221
* o remove trailing '.'
222
* o process trailing '..' ourselves
225
canonicalize_path(char *path)
228
bool was_sep = false;
232
* The Windows command processor will accept suitably quoted paths
233
* with forward slashes, but barfs badly with mixed forward and back
236
for (p = path; *p; p++)
243
* In Win32, if you do: prog.exe "a b" "\c\d\" the system will pass
244
* \c\d" as argv[2], so trim off trailing quote.
246
if (p > path && *(p - 1) == '"')
251
* Removing the trailing slash on a path means we never get ugly
252
* double trailing slashes. Also, Win32 can't stat() a directory
253
* with a trailing slash. Don't remove a leading slash, though.
255
trim_trailing_separator(path);
258
* Remove duplicate adjacent separators
262
/* Don't remove leading double-slash on Win32 */
267
for (; *p; p++, to_p++)
269
/* Handle many adjacent slashes, like "/a///b" */
270
while (*p == '/' && was_sep)
274
was_sep = (*p == '/');
279
* Remove any trailing uses of "." and process ".." ourselves
283
int len = strlen(path);
285
if (len > 2 && strcmp(path + len - 2, "/.") == 0)
286
trim_directory(path);
287
else if (len > 3 && strcmp(path + len - 3, "/..") == 0)
289
trim_directory(path);
290
trim_directory(path); /* remove directory above */
299
* Extracts the actual name of the program as called -
300
* stripped of .exe suffix if any
303
get_progname(const char *argv0)
305
const char *nodir_name;
307
nodir_name = last_dir_separator(argv0);
311
nodir_name = skip_drive(argv0);
313
#if defined(__CYGWIN__) || defined(WIN32)
314
/* strip .exe suffix, regardless of case */
315
if (strlen(nodir_name) > sizeof(EXE) - 1 &&
316
pg_strcasecmp(nodir_name + strlen(nodir_name)-(sizeof(EXE)-1), EXE) == 0)
320
progname = strdup(nodir_name);
321
if (progname == NULL)
323
fprintf(stderr, "%s: out of memory\n", nodir_name);
324
exit(1); /* This could exit the postmaster */
326
progname[strlen(progname) - (sizeof(EXE) - 1)] = '\0';
327
nodir_name = progname;
336
* make_relative_path - make a path relative to the actual binary location
338
* This function exists to support relocation of installation trees.
340
* ret_path is the output area (must be of size MAXPGPATH)
341
* target_path is the compiled-in path to the directory we want to find
342
* bin_path is the compiled-in path to the directory of executables
343
* my_exec_path is the actual location of my executable
345
* If target_path matches bin_path up to the last directory component of
346
* bin_path, then we build the result as my_exec_path (less the executable
347
* name and last directory) joined to the non-matching part of target_path.
348
* Otherwise, we return target_path as-is.
351
* target_path = '/usr/local/share/postgresql'
352
* bin_path = '/usr/local/bin'
353
* my_exec_path = '/opt/pgsql/bin/postmaster'
354
* Given these inputs we would return '/opt/pgsql/share/postgresql'
357
make_relative_path(char *ret_path, const char *target_path,
358
const char *bin_path, const char *my_exec_path)
363
bin_end = last_dir_separator(bin_path);
366
prefix_len = bin_end - bin_path + 1;
367
if (strncmp(target_path, bin_path, prefix_len) != 0)
370
StrNCpy(ret_path, my_exec_path, MAXPGPATH);
371
trim_directory(ret_path); /* remove my executable name */
372
trim_directory(ret_path); /* remove last directory component (/bin) */
373
join_path_components(ret_path, ret_path, target_path + prefix_len);
374
canonicalize_path(ret_path);
378
StrNCpy(ret_path, target_path, MAXPGPATH);
379
canonicalize_path(ret_path);
387
get_share_path(const char *my_exec_path, char *ret_path)
389
make_relative_path(ret_path, PGSHAREDIR, PGBINDIR, my_exec_path);
396
get_etc_path(const char *my_exec_path, char *ret_path)
398
make_relative_path(ret_path, SYSCONFDIR, PGBINDIR, my_exec_path);
405
get_include_path(const char *my_exec_path, char *ret_path)
407
make_relative_path(ret_path, INCLUDEDIR, PGBINDIR, my_exec_path);
411
* get_pkginclude_path
414
get_pkginclude_path(const char *my_exec_path, char *ret_path)
416
make_relative_path(ret_path, PKGINCLUDEDIR, PGBINDIR, my_exec_path);
420
* get_includeserver_path
423
get_includeserver_path(const char *my_exec_path, char *ret_path)
425
make_relative_path(ret_path, INCLUDEDIRSERVER, PGBINDIR, my_exec_path);
432
get_lib_path(const char *my_exec_path, char *ret_path)
434
make_relative_path(ret_path, LIBDIR, PGBINDIR, my_exec_path);
441
get_pkglib_path(const char *my_exec_path, char *ret_path)
443
make_relative_path(ret_path, PKGLIBDIR, PGBINDIR, my_exec_path);
450
get_locale_path(const char *my_exec_path, char *ret_path)
452
make_relative_path(ret_path, LOCALEDIR, PGBINDIR, my_exec_path);
459
* On Unix, this actually returns the user's home directory. On Windows
460
* it returns the PostgreSQL-specific application data folder.
463
get_home_path(char *ret_path)
467
struct passwd pwdstr;
468
struct passwd *pwd = NULL;
470
if (pqGetpwuid(geteuid(), &pwdstr, pwdbuf, sizeof(pwdbuf), &pwd) != 0)
472
StrNCpy(ret_path, pwd->pw_dir, MAXPGPATH);
476
char tmppath[MAX_PATH];
478
ZeroMemory(tmppath, sizeof(tmppath));
479
if (SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, 0, tmppath) != S_OK)
481
snprintf(ret_path, MAXPGPATH, "%s/postgresql", tmppath);
488
* get_parent_directory
490
* Modify the given string in-place to name the parent directory of the
494
get_parent_directory(char *path)
496
trim_directory(path);
501
* set_pglocale_pgservice
503
* Set application-specific locale and service directory
505
* This function takes an argv[0] rather than a full path.
508
set_pglocale_pgservice(const char *argv0, const char *app)
510
char path[MAXPGPATH];
511
char my_exec_path[MAXPGPATH];
512
char env_path[MAXPGPATH + sizeof("PGSYSCONFDIR=")]; /* longer than
515
/* don't set LC_ALL in the backend */
516
if (strcmp(app, "postgres") != 0)
517
setlocale(LC_ALL, "");
519
if (find_my_exec(argv0, my_exec_path) < 0)
523
get_locale_path(my_exec_path, path);
524
bindtextdomain(app, path);
527
if (getenv("PGLOCALEDIR") == NULL)
529
/* set for libpq to use */
530
snprintf(env_path, sizeof(env_path), "PGLOCALEDIR=%s", path);
531
canonicalize_path(env_path + 12);
532
putenv(strdup(env_path));
536
if (getenv("PGSYSCONFDIR") == NULL)
538
get_etc_path(my_exec_path, path);
540
/* set for libpq to use */
541
snprintf(env_path, sizeof(env_path), "PGSYSCONFDIR=%s", path);
542
canonicalize_path(env_path + 13);
543
putenv(strdup(env_path));
551
* Trim trailing directory from path, that is, remove any trailing slashes,
552
* the last pathname component, and the slash just ahead of it --- but never
553
* remove a leading slash.
556
trim_directory(char *path)
560
path = skip_drive(path);
565
/* back up over trailing slash(es) */
566
for (p = path + strlen(path) - 1; IS_DIR_SEP(*p) && p > path; p--)
568
/* back up over directory name */
569
for (; !IS_DIR_SEP(*p) && p > path; p--)
571
/* if multiple slashes before directory name, remove 'em all */
572
for (; p > path && IS_DIR_SEP(*(p - 1)); p--)
574
/* don't erase a leading slash */
575
if (p == path && IS_DIR_SEP(*p))
582
* trim_trailing_separator
584
* trim off trailing slashes, but not a leading slash
587
trim_trailing_separator(char *path)
591
path = skip_drive(path);
592
p = path + strlen(path);
594
for (p--; p > path && IS_DIR_SEP(*p); p--)