path.c 20.1 KB
Newer Older
1 2 3 4 5
/*-------------------------------------------------------------------------
 *
 * path.c
 *	  portable path handling routines
 *
B
Bruce Momjian 已提交
6
 * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
7 8 9 10
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
11
 *	  src/port/path.c
12 13 14
 *
 *-------------------------------------------------------------------------
 */
15

16 17 18 19 20
#ifndef FRONTEND
#include "postgres.h"
#else
#include "postgres_fe.h"
#endif
21

22
#include <ctype.h>
23
#include <sys/stat.h>
24
#ifdef WIN32
25 26 27
#ifdef _WIN32_IE
#undef _WIN32_IE
#endif
28
#define _WIN32_IE 0x0500
29 30 31 32
#ifdef near
#undef near
#endif
#define near
33 34
#include <shlobj.h>
#else
35 36
#include <unistd.h>
#endif
37

38 39 40
#include "pg_config_paths.h"


41
#ifndef WIN32
42
#define IS_PATH_VAR_SEP(ch) ((ch) == ':')
43
#else
44
#define IS_PATH_VAR_SEP(ch) ((ch) == ';')
45 46
#endif

47
static void make_relative_path(char *ret_path, const char *target_path,
B
Bruce Momjian 已提交
48
				   const char *bin_path, const char *my_exec_path);
49 50 51 52
static void trim_directory(char *path);
static void trim_trailing_separator(char *path);


53 54 55
/*
 * skip_drive
 *
B
Bruce Momjian 已提交
56
 * On Windows, a path may begin with "C:" or "//network/".  Advance over
57 58 59 60 61 62 63 64 65 66 67 68 69
 * 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++;
	}
70
	else if (isalpha((unsigned char) path[0]) && path[1] == ':')
71 72 73 74 75 76 77 78 79 80
	{
		path += 2;
	}
	return (char *) path;
}
#else

#define skip_drive(path)	(path)
#endif

R
Robert Haas 已提交
81 82 83 84 85 86 87 88 89 90 91
/*
 *	has_drive_prefix
 *
 * Return true if the given pathname has a drive prefix.
 */
bool
has_drive_prefix(const char *path)
{
	return skip_drive(path) != path;
}

92 93
/*
 *	first_dir_separator
94 95 96
 *
 * Find the location of the first directory separator, return
 * NULL if not found.
97 98 99 100
 */
char *
first_dir_separator(const char *filename)
{
101
	const char *p;
102

103
	for (p = skip_drive(filename); *p; p++)
104
		if (IS_DIR_SEP(*p))
105
			return (char *) p;
106 107 108
	return NULL;
}

109
/*
110
 *	first_path_var_separator
111 112 113
 *
 * Find the location of the first path separator (i.e. ':' on
 * Unix, ';' on Windows), return NULL if not found.
114
 */
B
Bruce Momjian 已提交
115
char *
116
first_path_var_separator(const char *pathlist)
117
{
118
	const char *p;
119

120 121
	/* skip_drive is not needed */
	for (p = pathlist; *p; p++)
122
		if (IS_PATH_VAR_SEP(*p))
123
			return (char *) p;
124
	return NULL;
125 126 127
}

/*
128
 *	last_dir_separator
129 130 131
 *
 * Find the location of the last directory separator, return
 * NULL if not found.
132
 */
B
Bruce Momjian 已提交
133
char *
134
last_dir_separator(const char *filename)
135
{
136
	const char *p,
B
Bruce Momjian 已提交
137
			   *ret = NULL;
138

139
	for (p = skip_drive(filename); *p; p++)
140
		if (IS_DIR_SEP(*p))
141
			ret = p;
142
	return (char *) ret;
143 144 145
}


146
/*
147 148
 *	make_native_path - on WIN32, change / to \ in the path
 *
149 150
 *	This effectively undoes canonicalize_path.
 *
151 152 153 154 155 156 157 158 159
 *	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
 *	does not convert forward to backward slashes, but COPY does
 *	properly process quoted forward slashes in the second argument.
 *
 *	COPY works with quoted forward slashes in the first argument
 *	only if the current directory is the same as the directory
 *	of the first argument.
160 161 162 163 164
 */
void
make_native_path(char *filename)
{
#ifdef WIN32
B
Bruce Momjian 已提交
165 166
	char	   *p;

167 168 169 170 171 172 173
	for (p = filename; *p; p++)
		if (*p == '/')
			*p = '\\';
#endif
}


J
Joe Conway 已提交
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
/*
 * This function cleans up the paths for use with either cmd.exe or Msys
 * on Windows. We need them to use filenames without spaces, for which a
 * short filename is the safest equivalent, eg:
 *		C:/Progra~1/
 */
void
cleanup_path(char *path)
{
#ifdef WIN32
	char	   *ptr;

	/*
	 * GetShortPathName() will fail if the path does not exist, or short names
	 * are disabled on this file system.  In both cases, we just return the
	 * original path.  This is particularly useful for --sysconfdir, which
	 * might not exist.
	 */
	GetShortPathName(path, path, MAXPGPATH - 1);

	/* Replace '\' with '/' */
	for (ptr = path; *ptr; ptr++)
	{
		if (*ptr == '\\')
			*ptr = '/';
	}
#endif
}


204 205 206
/*
 * join_path_components - join two path components, inserting a slash
 *
207 208
 * We omit the slash if either given component is empty.
 *
209 210 211 212 213 214 215 216 217
 * 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)
218
		strlcpy(ret_path, head, MAXPGPATH);
B
Bruce Momjian 已提交
219

220
	/*
221 222 223 224
	 * Remove any leading "." in the tail component.
	 *
	 * Note: we used to try to remove ".." as well, but that's tricky to get
	 * right; now we just leave it to be done by canonicalize_path() later.
225
	 */
226 227 228
	while (tail[0] == '.' && IS_DIR_SEP(tail[1]))
		tail += 2;

229
	if (*tail)
230 231
	{
		/* only separate with slash if head wasn't empty */
232
		snprintf(ret_path + strlen(ret_path), MAXPGPATH - strlen(ret_path),
233 234 235 236
				 "%s%s",
				 (*(skip_drive(head)) != '\0') ? "/" : "",
				 tail);
	}
237 238 239
}


240
/*
241 242
 *	Clean up path by:
 *		o  make Win32 path use Unix slashes
243 244
 *		o  remove trailing quote on Win32
 *		o  remove trailing slash
245
 *		o  remove duplicate adjacent separators
246 247
 *		o  remove trailing '.'
 *		o  process trailing '..' ourselves
248 249 250 251
 */
void
canonicalize_path(char *path)
{
B
Bruce Momjian 已提交
252 253
	char	   *p,
			   *to_p;
254
	char	   *spath;
255
	bool		was_sep = false;
256
	int			pending_strips;
B
Bruce Momjian 已提交
257

258
#ifdef WIN32
B
Bruce Momjian 已提交
259

260
	/*
B
Bruce Momjian 已提交
261 262
	 * The Windows command processor will accept suitably quoted paths with
	 * forward slashes, but barfs badly with mixed forward and back slashes.
263
	 */
264 265 266 267 268
	for (p = path; *p; p++)
	{
		if (*p == '\\')
			*p = '/';
	}
269

B
Bruce Momjian 已提交
270
	/*
B
Bruce Momjian 已提交
271 272
	 * In Win32, if you do: prog.exe "a b" "\c\d\" the system will pass \c\d"
	 * as argv[2], so trim off trailing quote.
273
	 */
B
Bruce Momjian 已提交
274 275
	if (p > path && *(p - 1) == '"')
		*(p - 1) = '/';
276 277
#endif

278
	/*
B
Bruce Momjian 已提交
279 280 281
	 * Removing the trailing slash on a path means we never get ugly double
	 * trailing slashes. Also, Win32 can't stat() a directory with a trailing
	 * slash. Don't remove a leading slash, though.
282
	 */
283
	trim_trailing_separator(path);
284

285
	/*
B
Bruce Momjian 已提交
286
	 * Remove duplicate adjacent separators
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305
	 */
	p = path;
#ifdef WIN32
	/* Don't remove leading double-slash on Win32 */
	if (*p)
		p++;
#endif
	to_p = p;
	for (; *p; p++, to_p++)
	{
		/* Handle many adjacent slashes, like "/a///b" */
		while (*p == '/' && was_sep)
			p++;
		if (to_p != p)
			*to_p = *p;
		was_sep = (*p == '/');
	}
	*to_p = '\0';

306
	/*
307
	 * Remove any trailing uses of "." and process ".." ourselves
308
	 *
309
	 * Note that "/../.." should reduce to just "/", while "../.." has to be
B
Bruce Momjian 已提交
310
	 * kept as-is.  In the latter case we put back mistakenly trimmed ".."
B
Bruce Momjian 已提交
311 312 313 314
	 * components below.  Also note that we want a Windows drive spec to be
	 * visible to trim_directory(), but it's not part of the logic that's
	 * looking at the name components; hence distinction between path and
	 * spath.
315
	 */
316 317
	spath = skip_drive(path);
	pending_strips = 0;
318 319
	for (;;)
	{
320
		int			len = strlen(spath);
321

322
		if (len >= 2 && strcmp(spath + len - 2, "/.") == 0)
323
			trim_directory(path);
324 325 326 327 328 329 330 331 332
		else if (strcmp(spath, ".") == 0)
		{
			/* Want to leave "." alone, but "./.." has to become ".." */
			if (pending_strips > 0)
				*spath = '\0';
			break;
		}
		else if ((len >= 3 && strcmp(spath + len - 3, "/..") == 0) ||
				 strcmp(spath, "..") == 0)
333 334
		{
			trim_directory(path);
335 336 337 338
			pending_strips++;
		}
		else if (pending_strips > 0 && *spath != '\0')
		{
339
			/* trim a regular directory name canceled by ".." */
340 341 342 343 344
			trim_directory(path);
			pending_strips--;
			/* foo/.. should become ".", not empty */
			if (*spath == '\0')
				strcpy(spath, ".");
345 346 347 348
		}
		else
			break;
	}
349 350 351 352

	if (pending_strips > 0)
	{
		/*
B
Bruce Momjian 已提交
353 354 355
		 * We could only get here if path is now totally empty (other than a
		 * possible drive specifier on Windows). We have to put back one or
		 * more ".."'s that we took off.
356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373
		 */
		while (--pending_strips > 0)
			strcat(path, "../");
		strcat(path, "..");
	}
}

/*
 * Detect whether a path contains any parent-directory references ("..")
 *
 * The input *must* have been put through canonicalize_path previously.
 *
 * This is a bit tricky because we mustn't be fooled by "..a.." (legal)
 * nor "C:.." (legal on Unix but not Windows).
 */
bool
path_contains_parent_reference(const char *path)
{
B
Bruce Momjian 已提交
374
	int			path_len;
375 376 377 378 379 380

	path = skip_drive(path);	/* C: shouldn't affect our conclusion */

	path_len = strlen(path);

	/*
B
Bruce Momjian 已提交
381 382
	 * ".." could be the whole path; otherwise, if it's present it must be at
	 * the beginning, in the middle, or at the end.
383 384 385 386 387 388 389 390
	 */
	if (strcmp(path, "..") == 0 ||
		strncmp(path, "../", 3) == 0 ||
		strstr(path, "/../") != NULL ||
		(path_len >= 3 && strcmp(path + path_len - 3, "/..") == 0))
		return true;

	return false;
391 392
}

393 394 395 396 397 398 399 400 401 402
/*
 * Detect whether a path is only in or below the current working directory.
 * An absolute path that matches the current working directory should
 * return false (we only want relative to the cwd).  We don't allow
 * "/../" even if that would keep us under the cwd (it is too hard to
 * track that).
 */
bool
path_is_relative_and_below_cwd(const char *path)
{
403
	if (is_absolute_path(path))
404 405 406 407 408
		return false;
	/* don't allow anything above the cwd */
	else if (path_contains_parent_reference(path))
		return false;
#ifdef WIN32
B
Bruce Momjian 已提交
409

410
	/*
411 412 413 414 415 416 417
	 * On Win32, a drive letter _not_ followed by a slash, e.g. 'E:abc', is
	 * relative to the cwd on that drive, or the drive's root directory if
	 * that drive has no cwd.  Because the path itself cannot tell us which is
	 * the case, we have to assume the worst, i.e. that it is not below the
	 * cwd.  We could use GetFullPathName() to find the full path but that
	 * could change if the current directory for the drive changes underneath
	 * us, so we just disallow it.
418 419
	 */
	else if (isalpha((unsigned char) path[0]) && path[1] == ':' &&
420
			 !IS_DIR_SEP(path[2]))
421 422 423
		return false;
#endif
	else
424
		return true;
425 426
}

427 428 429 430 431 432 433 434 435
/*
 * Detect whether path1 is a prefix of path2 (including equality).
 *
 * This is pretty trivial, but it seems better to export a function than
 * to export IS_DIR_SEP.
 */
bool
path_is_prefix_of_path(const char *path1, const char *path2)
{
B
Bruce Momjian 已提交
436
	int			path1_len = strlen(path1);
437 438 439 440 441 442

	if (strncmp(path1, path2, path1_len) == 0 &&
		(IS_DIR_SEP(path2[path1_len]) || path2[path1_len] == '\0'))
		return true;
	return false;
}
443

444
/*
B
Bruce Momjian 已提交
445
 * Extracts the actual name of the program as called -
446
 * stripped of .exe suffix if any
447
 */
448 449
const char *
get_progname(const char *argv0)
450
{
B
Bruce Momjian 已提交
451 452
	const char *nodir_name;
	char	   *progname;
453

454 455 456
	nodir_name = last_dir_separator(argv0);
	if (nodir_name)
		nodir_name++;
457
	else
458
		nodir_name = skip_drive(argv0);
459

460
	/*
B
Bruce Momjian 已提交
461 462
	 * Make a copy in case argv[0] is modified by ps_status. Leaks memory, but
	 * called only once.
463 464 465
	 */
	progname = strdup(nodir_name);
	if (progname == NULL)
466
	{
467
		fprintf(stderr, "%s: out of memory\n", nodir_name);
468
		abort();				/* This could exit the postmaster */
469
	}
470

471 472 473
#if defined(__CYGWIN__) || defined(WIN32)
	/* strip ".exe" suffix, regardless of case */
	if (strlen(progname) > sizeof(EXE) - 1 &&
B
Bruce Momjian 已提交
474
	pg_strcasecmp(progname + strlen(progname) - (sizeof(EXE) - 1), EXE) == 0)
475
		progname[strlen(progname) - (sizeof(EXE) - 1)] = '\0';
476 477
#endif

478
	return progname;
479
}
480

481

482
/*
483 484
 * dir_strcmp: strcmp except any two DIR_SEP characters are considered equal,
 * and we honor filesystem case insensitivity if known
485 486 487 488 489 490
 */
static int
dir_strcmp(const char *s1, const char *s2)
{
	while (*s1 && *s2)
	{
491
		if (
492
#ifndef WIN32
493
			*s1 != *s2
494
#else
495
		/* On windows, paths are case-insensitive */
496
			pg_tolower((unsigned char) *s1) != pg_tolower((unsigned char) *s2)
497
#endif
498
			&& !(IS_DIR_SEP(*s1) && IS_DIR_SEP(*s2)))
499 500 501 502 503 504 505 506 507 508 509
			return (int) *s1 - (int) *s2;
		s1++, s2++;
	}
	if (*s1)
		return 1;				/* s1 longer */
	if (*s2)
		return -1;				/* s2 longer */
	return 0;
}


510
/*
511 512 513 514 515 516 517 518 519
 * make_relative_path - make a path relative to the actual binary location
 *
 * This function exists to support relocation of installation trees.
 *
 *	ret_path is the output area (must be of size MAXPGPATH)
 *	target_path is the compiled-in path to the directory we want to find
 *	bin_path is the compiled-in path to the directory of executables
 *	my_exec_path is the actual location of my executable
 *
520 521 522 523 524
 * We determine the common prefix of target_path and bin_path, then compare
 * the remainder of bin_path to the last directory component(s) of
 * my_exec_path.  If they match, build the result as the part of my_exec_path
 * preceding the match, joined to the remainder of target_path.  If no match,
 * return target_path as-is.
B
Bruce Momjian 已提交
525
 *
526 527
 * For example:
 *		target_path  = '/usr/local/share/postgresql'
B
Bruce Momjian 已提交
528
 *		bin_path	 = '/usr/local/bin'
529
 *		my_exec_path = '/opt/pgsql/bin/postmaster'
530 531 532
 * Given these inputs, the common prefix is '/usr/local/', the tail of
 * bin_path is 'bin' which does match the last directory component of
 * my_exec_path, so we would return '/opt/pgsql/share/postgresql'
533
 */
534 535 536
static void
make_relative_path(char *ret_path, const char *target_path,
				   const char *bin_path, const char *my_exec_path)
537
{
538
	int			prefix_len;
539 540 541
	int			tail_start;
	int			tail_len;
	int			i;
542

543 544 545 546 547 548 549 550 551 552 553 554 555 556 557
	/*
	 * Determine the common prefix --- note we require it to end on a
	 * directory separator, consider eg '/usr/lib' and '/usr/libexec'.
	 */
	prefix_len = 0;
	for (i = 0; target_path[i] && bin_path[i]; i++)
	{
		if (IS_DIR_SEP(target_path[i]) && IS_DIR_SEP(bin_path[i]))
			prefix_len = i + 1;
		else if (target_path[i] != bin_path[i])
			break;
	}
	if (prefix_len == 0)
		goto no_match;			/* no common prefix? */
	tail_len = strlen(bin_path) - prefix_len;
558

559 560 561 562
	/*
	 * Set up my_exec_path without the actual executable name, and
	 * canonicalize to simplify comparison to bin_path.
	 */
563
	strlcpy(ret_path, my_exec_path, MAXPGPATH);
564 565
	trim_directory(ret_path);	/* remove my executable name */
	canonicalize_path(ret_path);
566 567 568 569 570 571

	/*
	 * Tail match?
	 */
	tail_start = (int) strlen(ret_path) - tail_len;
	if (tail_start > 0 &&
B
Bruce Momjian 已提交
572
		IS_DIR_SEP(ret_path[tail_start - 1]) &&
573 574 575 576 577 578 579 580
		dir_strcmp(ret_path + tail_start, bin_path + prefix_len) == 0)
	{
		ret_path[tail_start] = '\0';
		trim_trailing_separator(ret_path);
		join_path_components(ret_path, ret_path, target_path + prefix_len);
		canonicalize_path(ret_path);
		return;
	}
B
Bruce Momjian 已提交
581

582
no_match:
583
	strlcpy(ret_path, target_path, MAXPGPATH);
584
	canonicalize_path(ret_path);
585 586 587
}


588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642
/*
 * make_absolute_path
 *
 * If the given pathname isn't already absolute, make it so, interpreting
 * it relative to the current working directory.
 *
 * Also canonicalizes the path.  The result is always a malloc'd copy.
 *
 * In backend, failure cases result in ereport(ERROR); in frontend,
 * we write a complaint on stderr and return NULL.
 *
 * Note: interpretation of relative-path arguments during postmaster startup
 * should happen before doing ChangeToDataDir(), else the user will probably
 * not like the results.
 */
char *
make_absolute_path(const char *path)
{
	char	   *new;

	/* Returning null for null input is convenient for some callers */
	if (path == NULL)
		return NULL;

	if (!is_absolute_path(path))
	{
		char	   *buf;
		size_t		buflen;

		buflen = MAXPGPATH;
		for (;;)
		{
			buf = malloc(buflen);
			if (!buf)
			{
#ifndef FRONTEND
				ereport(ERROR,
						(errcode(ERRCODE_OUT_OF_MEMORY),
						 errmsg("out of memory")));
#else
				fprintf(stderr, _("out of memory\n"));
				return NULL;
#endif
			}

			if (getcwd(buf, buflen))
				break;
			else if (errno == ERANGE)
			{
				free(buf);
				buflen *= 2;
				continue;
			}
			else
			{
T
Tom Lane 已提交
643 644
				int			save_errno = errno;

645
				free(buf);
T
Tom Lane 已提交
646
				errno = save_errno;
647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695
#ifndef FRONTEND
				elog(ERROR, "could not get current working directory: %m");
#else
				fprintf(stderr, _("could not get current working directory: %s\n"),
						strerror(errno));
				return NULL;
#endif
			}
		}

		new = malloc(strlen(buf) + strlen(path) + 2);
		if (!new)
		{
			free(buf);
#ifndef FRONTEND
			ereport(ERROR,
					(errcode(ERRCODE_OUT_OF_MEMORY),
					 errmsg("out of memory")));
#else
			fprintf(stderr, _("out of memory\n"));
			return NULL;
#endif
		}
		sprintf(new, "%s/%s", buf, path);
		free(buf);
	}
	else
	{
		new = strdup(path);
		if (!new)
		{
#ifndef FRONTEND
			ereport(ERROR,
					(errcode(ERRCODE_OUT_OF_MEMORY),
					 errmsg("out of memory")));
#else
			fprintf(stderr, _("out of memory\n"));
			return NULL;
#endif
		}
	}

	/* Make sure punctuation is canonical, too */
	canonicalize_path(new);

	return new;
}


696 697 698 699 700 701 702 703 704
/*
 *	get_share_path
 */
void
get_share_path(const char *my_exec_path, char *ret_path)
{
	make_relative_path(ret_path, PGSHAREDIR, PGBINDIR, my_exec_path);
}

705 706 707 708 709 710
/*
 *	get_etc_path
 */
void
get_etc_path(const char *my_exec_path, char *ret_path)
{
711
	make_relative_path(ret_path, SYSCONFDIR, PGBINDIR, my_exec_path);
712 713 714 715 716 717 718 719
}

/*
 *	get_include_path
 */
void
get_include_path(const char *my_exec_path, char *ret_path)
{
720
	make_relative_path(ret_path, INCLUDEDIR, PGBINDIR, my_exec_path);
721 722 723 724 725 726 727 728
}

/*
 *	get_pkginclude_path
 */
void
get_pkginclude_path(const char *my_exec_path, char *ret_path)
{
729
	make_relative_path(ret_path, PKGINCLUDEDIR, PGBINDIR, my_exec_path);
730 731
}

B
Bruce Momjian 已提交
732 733 734 735 736 737
/*
 *	get_includeserver_path
 */
void
get_includeserver_path(const char *my_exec_path, char *ret_path)
{
738
	make_relative_path(ret_path, INCLUDEDIRSERVER, PGBINDIR, my_exec_path);
B
Bruce Momjian 已提交
739 740 741 742 743 744 745 746
}

/*
 *	get_lib_path
 */
void
get_lib_path(const char *my_exec_path, char *ret_path)
{
747
	make_relative_path(ret_path, LIBDIR, PGBINDIR, my_exec_path);
B
Bruce Momjian 已提交
748 749
}

750 751 752 753 754 755
/*
 *	get_pkglib_path
 */
void
get_pkglib_path(const char *my_exec_path, char *ret_path)
{
756
	make_relative_path(ret_path, PKGLIBDIR, PGBINDIR, my_exec_path);
757 758
}

759 760 761 762 763 764
/*
 *	get_locale_path
 */
void
get_locale_path(const char *my_exec_path, char *ret_path)
{
765
	make_relative_path(ret_path, LOCALEDIR, PGBINDIR, my_exec_path);
766 767
}

768 769 770 771 772 773 774 775 776
/*
 *	get_doc_path
 */
void
get_doc_path(const char *my_exec_path, char *ret_path)
{
	make_relative_path(ret_path, DOCDIR, PGBINDIR, my_exec_path);
}

777 778 779 780 781 782 783 784 785
/*
 *	get_html_path
 */
void
get_html_path(const char *my_exec_path, char *ret_path)
{
	make_relative_path(ret_path, HTMLDIR, PGBINDIR, my_exec_path);
}

786 787 788 789 790 791 792 793 794
/*
 *	get_man_path
 */
void
get_man_path(const char *my_exec_path, char *ret_path)
{
	make_relative_path(ret_path, MANDIR, PGBINDIR, my_exec_path);
}

795

796 797
/*
 *	get_home_path
798 799 800
 *
 * On Unix, this actually returns the user's home directory.  On Windows
 * it returns the PostgreSQL-specific application data folder.
801 802 803 804
 */
bool
get_home_path(char *ret_path)
{
805 806 807 808 809
#ifndef WIN32
	char		pwdbuf[BUFSIZ];
	struct passwd pwdstr;
	struct passwd *pwd = NULL;

810 811
	(void) pqGetpwuid(geteuid(), &pwdstr, pwdbuf, sizeof(pwdbuf), &pwd);
	if (pwd == NULL)
812
		return false;
813
	strlcpy(ret_path, pwd->pw_dir, MAXPGPATH);
814 815
	return true;
#else
B
Bruce Momjian 已提交
816
	char	   *tmppath;
817

B
Bruce Momjian 已提交
818
	/*
819 820 821 822 823
	 * Note: We use getenv() here because the more modern SHGetFolderPath()
	 * would force the backend to link with shell32.lib, which eats valuable
	 * desktop heap.  XXX This function is used only in psql, which already
	 * brings in shell32 via libpq.  Moving this function to its own file
	 * would keep it out of the backend, freeing it from this concern.
B
Bruce Momjian 已提交
824 825
	 */
	tmppath = getenv("APPDATA");
826
	if (!tmppath)
827
		return false;
828
	snprintf(ret_path, MAXPGPATH, "%s/postgresql", tmppath);
829 830
	return true;
#endif
831 832 833 834 835 836 837 838
}


/*
 * get_parent_directory
 *
 * Modify the given string in-place to name the parent directory of the
 * named file.
839 840 841 842 843 844 845 846 847
 *
 * If the input is just a file name with no directory part, the result is
 * an empty string, not ".".  This is appropriate when the next step is
 * join_path_components(), but might need special handling otherwise.
 *
 * Caution: this will not produce desirable results if the string ends
 * with "..".  For most callers this is not a problem since the string
 * is already known to name a regular file.  If in doubt, apply
 * canonicalize_path() first.
848 849 850 851 852 853 854
 */
void
get_parent_directory(char *path)
{
	trim_directory(path);
}

855

856 857 858
/*
 *	trim_directory
 *
859 860 861
 *	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.
862 863 864 865
 */
static void
trim_directory(char *path)
{
B
Bruce Momjian 已提交
866 867
	char	   *p;

868 869
	path = skip_drive(path);

870 871 872
	if (path[0] == '\0')
		return;

873
	/* back up over trailing slash(es) */
874
	for (p = path + strlen(path) - 1; IS_DIR_SEP(*p) && p > path; p--)
875
		;
876
	/* back up over directory name */
877
	for (; !IS_DIR_SEP(*p) && p > path; p--)
878
		;
879 880 881 882 883 884
	/* 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++;
885 886 887 888 889 890
	*p = '\0';
}


/*
 *	trim_trailing_separator
891 892
 *
 * trim off trailing slashes, but not a leading slash
893 894 895 896
 */
static void
trim_trailing_separator(char *path)
{
897
	char	   *p;
898

899 900
	path = skip_drive(path);
	p = path + strlen(path);
901
	if (p > path)
902
		for (p--; p > path && IS_DIR_SEP(*p); p--)
903 904
			*p = '\0';
}