1 /*-------------------------------------------------------------------------
2  *
3  * path.c
4  *	  portable path handling routines
5  *
6  * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *	  src/port/path.c
12  *
13  *-------------------------------------------------------------------------
14  */
15 
16 #ifndef FRONTEND
17 #include "postgres.h"
18 #else
19 #include "postgres_fe.h"
20 #endif
21 
22 #include <ctype.h>
23 #include <sys/stat.h>
24 #ifdef WIN32
25 #ifdef _WIN32_IE
26 #undef _WIN32_IE
27 #endif
28 #define _WIN32_IE 0x0500
29 #ifdef near
30 #undef near
31 #endif
32 #define near
33 #include <shlobj.h>
34 #else
35 #include <unistd.h>
36 #endif
37 
38 #include "pg_config_paths.h"
39 
40 
41 #ifndef WIN32
42 #define IS_PATH_VAR_SEP(ch) ((ch) == ':')
43 #else
44 #define IS_PATH_VAR_SEP(ch) ((ch) == ';')
45 #endif
46 
47 static void make_relative_path(char *ret_path, const char *target_path,
48 							   const char *bin_path, const char *my_exec_path);
49 static void trim_directory(char *path);
50 static void trim_trailing_separator(char *path);
51 
52 
53 /*
54  * skip_drive
55  *
56  * On Windows, a path may begin with "C:" or "//network/".  Advance over
57  * this and point to the effective start of the path.
58  */
59 #ifdef WIN32
60 
61 static char *
skip_drive(const char * path)62 skip_drive(const char *path)
63 {
64 	if (IS_DIR_SEP(path[0]) && IS_DIR_SEP(path[1]))
65 	{
66 		path += 2;
67 		while (*path && !IS_DIR_SEP(*path))
68 			path++;
69 	}
70 	else if (isalpha((unsigned char) path[0]) && path[1] == ':')
71 	{
72 		path += 2;
73 	}
74 	return (char *) path;
75 }
76 #else
77 
78 #define skip_drive(path)	(path)
79 #endif
80 
81 /*
82  *	has_drive_prefix
83  *
84  * Return true if the given pathname has a drive prefix.
85  */
86 bool
has_drive_prefix(const char * path)87 has_drive_prefix(const char *path)
88 {
89 #ifdef WIN32
90 	return skip_drive(path) != path;
91 #else
92 	return false;
93 #endif
94 }
95 
96 /*
97  *	first_dir_separator
98  *
99  * Find the location of the first directory separator, return
100  * NULL if not found.
101  */
102 char *
first_dir_separator(const char * filename)103 first_dir_separator(const char *filename)
104 {
105 	const char *p;
106 
107 	for (p = skip_drive(filename); *p; p++)
108 		if (IS_DIR_SEP(*p))
109 			return unconstify(char *, p);
110 	return NULL;
111 }
112 
113 /*
114  *	first_path_var_separator
115  *
116  * Find the location of the first path separator (i.e. ':' on
117  * Unix, ';' on Windows), return NULL if not found.
118  */
119 char *
first_path_var_separator(const char * pathlist)120 first_path_var_separator(const char *pathlist)
121 {
122 	const char *p;
123 
124 	/* skip_drive is not needed */
125 	for (p = pathlist; *p; p++)
126 		if (IS_PATH_VAR_SEP(*p))
127 			return unconstify(char *, p);
128 	return NULL;
129 }
130 
131 /*
132  *	last_dir_separator
133  *
134  * Find the location of the last directory separator, return
135  * NULL if not found.
136  */
137 char *
last_dir_separator(const char * filename)138 last_dir_separator(const char *filename)
139 {
140 	const char *p,
141 			   *ret = NULL;
142 
143 	for (p = skip_drive(filename); *p; p++)
144 		if (IS_DIR_SEP(*p))
145 			ret = p;
146 	return unconstify(char *, ret);
147 }
148 
149 
150 /*
151  *	make_native_path - on WIN32, change / to \ in the path
152  *
153  *	This effectively undoes canonicalize_path.
154  *
155  *	This is required because WIN32 COPY is an internal CMD.EXE
156  *	command and doesn't process forward slashes in the same way
157  *	as external commands.  Quoting the first argument to COPY
158  *	does not convert forward to backward slashes, but COPY does
159  *	properly process quoted forward slashes in the second argument.
160  *
161  *	COPY works with quoted forward slashes in the first argument
162  *	only if the current directory is the same as the directory
163  *	of the first argument.
164  */
165 void
make_native_path(char * filename)166 make_native_path(char *filename)
167 {
168 #ifdef WIN32
169 	char	   *p;
170 
171 	for (p = filename; *p; p++)
172 		if (*p == '/')
173 			*p = '\\';
174 #endif
175 }
176 
177 
178 /*
179  * This function cleans up the paths for use with either cmd.exe or Msys
180  * on Windows. We need them to use filenames without spaces, for which a
181  * short filename is the safest equivalent, eg:
182  *		C:/Progra~1/
183  */
184 void
cleanup_path(char * path)185 cleanup_path(char *path)
186 {
187 #ifdef WIN32
188 	char	   *ptr;
189 
190 	/*
191 	 * GetShortPathName() will fail if the path does not exist, or short names
192 	 * are disabled on this file system.  In both cases, we just return the
193 	 * original path.  This is particularly useful for --sysconfdir, which
194 	 * might not exist.
195 	 */
196 	GetShortPathName(path, path, MAXPGPATH - 1);
197 
198 	/* Replace '\' with '/' */
199 	for (ptr = path; *ptr; ptr++)
200 	{
201 		if (*ptr == '\\')
202 			*ptr = '/';
203 	}
204 #endif
205 }
206 
207 
208 /*
209  * join_path_components - join two path components, inserting a slash
210  *
211  * We omit the slash if either given component is empty.
212  *
213  * ret_path is the output area (must be of size MAXPGPATH)
214  *
215  * ret_path can be the same as head, but not the same as tail.
216  */
217 void
join_path_components(char * ret_path,const char * head,const char * tail)218 join_path_components(char *ret_path,
219 					 const char *head, const char *tail)
220 {
221 	if (ret_path != head)
222 		strlcpy(ret_path, head, MAXPGPATH);
223 
224 	/*
225 	 * Remove any leading "." in the tail component.
226 	 *
227 	 * Note: we used to try to remove ".." as well, but that's tricky to get
228 	 * right; now we just leave it to be done by canonicalize_path() later.
229 	 */
230 	while (tail[0] == '.' && IS_DIR_SEP(tail[1]))
231 		tail += 2;
232 
233 	if (*tail)
234 	{
235 		/* only separate with slash if head wasn't empty */
236 		snprintf(ret_path + strlen(ret_path), MAXPGPATH - strlen(ret_path),
237 				 "%s%s",
238 				 (*(skip_drive(head)) != '\0') ? "/" : "",
239 				 tail);
240 	}
241 }
242 
243 
244 /*
245  *	Clean up path by:
246  *		o  make Win32 path use Unix slashes
247  *		o  remove trailing quote on Win32
248  *		o  remove trailing slash
249  *		o  remove duplicate adjacent separators
250  *		o  remove trailing '.'
251  *		o  process trailing '..' ourselves
252  */
253 void
canonicalize_path(char * path)254 canonicalize_path(char *path)
255 {
256 	char	   *p,
257 			   *to_p;
258 	char	   *spath;
259 	bool		was_sep = false;
260 	int			pending_strips;
261 
262 #ifdef WIN32
263 
264 	/*
265 	 * The Windows command processor will accept suitably quoted paths with
266 	 * forward slashes, but barfs badly with mixed forward and back slashes.
267 	 */
268 	for (p = path; *p; p++)
269 	{
270 		if (*p == '\\')
271 			*p = '/';
272 	}
273 
274 	/*
275 	 * In Win32, if you do: prog.exe "a b" "\c\d\" the system will pass \c\d"
276 	 * as argv[2], so trim off trailing quote.
277 	 */
278 	if (p > path && *(p - 1) == '"')
279 		*(p - 1) = '/';
280 #endif
281 
282 	/*
283 	 * Removing the trailing slash on a path means we never get ugly double
284 	 * trailing slashes. Also, Win32 can't stat() a directory with a trailing
285 	 * slash. Don't remove a leading slash, though.
286 	 */
287 	trim_trailing_separator(path);
288 
289 	/*
290 	 * Remove duplicate adjacent separators
291 	 */
292 	p = path;
293 #ifdef WIN32
294 	/* Don't remove leading double-slash on Win32 */
295 	if (*p)
296 		p++;
297 #endif
298 	to_p = p;
299 	for (; *p; p++, to_p++)
300 	{
301 		/* Handle many adjacent slashes, like "/a///b" */
302 		while (*p == '/' && was_sep)
303 			p++;
304 		if (to_p != p)
305 			*to_p = *p;
306 		was_sep = (*p == '/');
307 	}
308 	*to_p = '\0';
309 
310 	/*
311 	 * Remove any trailing uses of "." and process ".." ourselves
312 	 *
313 	 * Note that "/../.." should reduce to just "/", while "../.." has to be
314 	 * kept as-is.  In the latter case we put back mistakenly trimmed ".."
315 	 * components below.  Also note that we want a Windows drive spec to be
316 	 * visible to trim_directory(), but it's not part of the logic that's
317 	 * looking at the name components; hence distinction between path and
318 	 * spath.
319 	 */
320 	spath = skip_drive(path);
321 	pending_strips = 0;
322 	for (;;)
323 	{
324 		int			len = strlen(spath);
325 
326 		if (len >= 2 && strcmp(spath + len - 2, "/.") == 0)
327 			trim_directory(path);
328 		else if (strcmp(spath, ".") == 0)
329 		{
330 			/* Want to leave "." alone, but "./.." has to become ".." */
331 			if (pending_strips > 0)
332 				*spath = '\0';
333 			break;
334 		}
335 		else if ((len >= 3 && strcmp(spath + len - 3, "/..") == 0) ||
336 				 strcmp(spath, "..") == 0)
337 		{
338 			trim_directory(path);
339 			pending_strips++;
340 		}
341 		else if (pending_strips > 0 && *spath != '\0')
342 		{
343 			/* trim a regular directory name canceled by ".." */
344 			trim_directory(path);
345 			pending_strips--;
346 			/* foo/.. should become ".", not empty */
347 			if (*spath == '\0')
348 				strcpy(spath, ".");
349 		}
350 		else
351 			break;
352 	}
353 
354 	if (pending_strips > 0)
355 	{
356 		/*
357 		 * We could only get here if path is now totally empty (other than a
358 		 * possible drive specifier on Windows). We have to put back one or
359 		 * more ".."'s that we took off.
360 		 */
361 		while (--pending_strips > 0)
362 			strcat(path, "../");
363 		strcat(path, "..");
364 	}
365 }
366 
367 /*
368  * Detect whether a path contains any parent-directory references ("..")
369  *
370  * The input *must* have been put through canonicalize_path previously.
371  *
372  * This is a bit tricky because we mustn't be fooled by "..a.." (legal)
373  * nor "C:.." (legal on Unix but not Windows).
374  */
375 bool
path_contains_parent_reference(const char * path)376 path_contains_parent_reference(const char *path)
377 {
378 	int			path_len;
379 
380 	path = skip_drive(path);	/* C: shouldn't affect our conclusion */
381 
382 	path_len = strlen(path);
383 
384 	/*
385 	 * ".." could be the whole path; otherwise, if it's present it must be at
386 	 * the beginning, in the middle, or at the end.
387 	 */
388 	if (strcmp(path, "..") == 0 ||
389 		strncmp(path, "../", 3) == 0 ||
390 		strstr(path, "/../") != NULL ||
391 		(path_len >= 3 && strcmp(path + path_len - 3, "/..") == 0))
392 		return true;
393 
394 	return false;
395 }
396 
397 /*
398  * Detect whether a path is only in or below the current working directory.
399  * An absolute path that matches the current working directory should
400  * return false (we only want relative to the cwd).  We don't allow
401  * "/../" even if that would keep us under the cwd (it is too hard to
402  * track that).
403  */
404 bool
path_is_relative_and_below_cwd(const char * path)405 path_is_relative_and_below_cwd(const char *path)
406 {
407 	if (is_absolute_path(path))
408 		return false;
409 	/* don't allow anything above the cwd */
410 	else if (path_contains_parent_reference(path))
411 		return false;
412 #ifdef WIN32
413 
414 	/*
415 	 * On Win32, a drive letter _not_ followed by a slash, e.g. 'E:abc', is
416 	 * relative to the cwd on that drive, or the drive's root directory if
417 	 * that drive has no cwd.  Because the path itself cannot tell us which is
418 	 * the case, we have to assume the worst, i.e. that it is not below the
419 	 * cwd.  We could use GetFullPathName() to find the full path but that
420 	 * could change if the current directory for the drive changes underneath
421 	 * us, so we just disallow it.
422 	 */
423 	else if (isalpha((unsigned char) path[0]) && path[1] == ':' &&
424 			 !IS_DIR_SEP(path[2]))
425 		return false;
426 #endif
427 	else
428 		return true;
429 }
430 
431 /*
432  * Detect whether path1 is a prefix of path2 (including equality).
433  *
434  * This is pretty trivial, but it seems better to export a function than
435  * to export IS_DIR_SEP.
436  */
437 bool
path_is_prefix_of_path(const char * path1,const char * path2)438 path_is_prefix_of_path(const char *path1, const char *path2)
439 {
440 	int			path1_len = strlen(path1);
441 
442 	if (strncmp(path1, path2, path1_len) == 0 &&
443 		(IS_DIR_SEP(path2[path1_len]) || path2[path1_len] == '\0'))
444 		return true;
445 	return false;
446 }
447 
448 /*
449  * Extracts the actual name of the program as called -
450  * stripped of .exe suffix if any
451  */
452 const char *
get_progname(const char * argv0)453 get_progname(const char *argv0)
454 {
455 	const char *nodir_name;
456 	char	   *progname;
457 
458 	nodir_name = last_dir_separator(argv0);
459 	if (nodir_name)
460 		nodir_name++;
461 	else
462 		nodir_name = skip_drive(argv0);
463 
464 	/*
465 	 * Make a copy in case argv[0] is modified by ps_status. Leaks memory, but
466 	 * called only once.
467 	 */
468 	progname = strdup(nodir_name);
469 	if (progname == NULL)
470 	{
471 		fprintf(stderr, "%s: out of memory\n", nodir_name);
472 		abort();				/* This could exit the postmaster */
473 	}
474 
475 #if defined(__CYGWIN__) || defined(WIN32)
476 	/* strip ".exe" suffix, regardless of case */
477 	if (strlen(progname) > sizeof(EXE) - 1 &&
478 		pg_strcasecmp(progname + strlen(progname) - (sizeof(EXE) - 1), EXE) == 0)
479 		progname[strlen(progname) - (sizeof(EXE) - 1)] = '\0';
480 #endif
481 
482 	return progname;
483 }
484 
485 
486 /*
487  * dir_strcmp: strcmp except any two DIR_SEP characters are considered equal,
488  * and we honor filesystem case insensitivity if known
489  */
490 static int
dir_strcmp(const char * s1,const char * s2)491 dir_strcmp(const char *s1, const char *s2)
492 {
493 	while (*s1 && *s2)
494 	{
495 		if (
496 #ifndef WIN32
497 			*s1 != *s2
498 #else
499 		/* On windows, paths are case-insensitive */
500 			pg_tolower((unsigned char) *s1) != pg_tolower((unsigned char) *s2)
501 #endif
502 			&& !(IS_DIR_SEP(*s1) && IS_DIR_SEP(*s2)))
503 			return (int) *s1 - (int) *s2;
504 		s1++, s2++;
505 	}
506 	if (*s1)
507 		return 1;				/* s1 longer */
508 	if (*s2)
509 		return -1;				/* s2 longer */
510 	return 0;
511 }
512 
513 
514 /*
515  * make_relative_path - make a path relative to the actual binary location
516  *
517  * This function exists to support relocation of installation trees.
518  *
519  *	ret_path is the output area (must be of size MAXPGPATH)
520  *	target_path is the compiled-in path to the directory we want to find
521  *	bin_path is the compiled-in path to the directory of executables
522  *	my_exec_path is the actual location of my executable
523  *
524  * We determine the common prefix of target_path and bin_path, then compare
525  * the remainder of bin_path to the last directory component(s) of
526  * my_exec_path.  If they match, build the result as the part of my_exec_path
527  * preceding the match, joined to the remainder of target_path.  If no match,
528  * return target_path as-is.
529  *
530  * For example:
531  *		target_path  = '/usr/local/share/postgresql'
532  *		bin_path	 = '/usr/local/bin'
533  *		my_exec_path = '/opt/pgsql/bin/postmaster'
534  * Given these inputs, the common prefix is '/usr/local/', the tail of
535  * bin_path is 'bin' which does match the last directory component of
536  * my_exec_path, so we would return '/opt/pgsql/share/postgresql'
537  */
538 static void
make_relative_path(char * ret_path,const char * target_path,const char * bin_path,const char * my_exec_path)539 make_relative_path(char *ret_path, const char *target_path,
540 				   const char *bin_path, const char *my_exec_path)
541 {
542 	int			prefix_len;
543 	int			tail_start;
544 	int			tail_len;
545 	int			i;
546 
547 	/*
548 	 * Determine the common prefix --- note we require it to end on a
549 	 * directory separator, consider eg '/usr/lib' and '/usr/libexec'.
550 	 */
551 	prefix_len = 0;
552 	for (i = 0; target_path[i] && bin_path[i]; i++)
553 	{
554 		if (IS_DIR_SEP(target_path[i]) && IS_DIR_SEP(bin_path[i]))
555 			prefix_len = i + 1;
556 		else if (target_path[i] != bin_path[i])
557 			break;
558 	}
559 	if (prefix_len == 0)
560 		goto no_match;			/* no common prefix? */
561 	tail_len = strlen(bin_path) - prefix_len;
562 
563 	/*
564 	 * Set up my_exec_path without the actual executable name, and
565 	 * canonicalize to simplify comparison to bin_path.
566 	 */
567 	strlcpy(ret_path, my_exec_path, MAXPGPATH);
568 	trim_directory(ret_path);	/* remove my executable name */
569 	canonicalize_path(ret_path);
570 
571 	/*
572 	 * Tail match?
573 	 */
574 	tail_start = (int) strlen(ret_path) - tail_len;
575 	if (tail_start > 0 &&
576 		IS_DIR_SEP(ret_path[tail_start - 1]) &&
577 		dir_strcmp(ret_path + tail_start, bin_path + prefix_len) == 0)
578 	{
579 		ret_path[tail_start] = '\0';
580 		trim_trailing_separator(ret_path);
581 		join_path_components(ret_path, ret_path, target_path + prefix_len);
582 		canonicalize_path(ret_path);
583 		return;
584 	}
585 
586 no_match:
587 	strlcpy(ret_path, target_path, MAXPGPATH);
588 	canonicalize_path(ret_path);
589 }
590 
591 
592 /*
593  * make_absolute_path
594  *
595  * If the given pathname isn't already absolute, make it so, interpreting
596  * it relative to the current working directory.
597  *
598  * Also canonicalizes the path.  The result is always a malloc'd copy.
599  *
600  * In backend, failure cases result in ereport(ERROR); in frontend,
601  * we write a complaint on stderr and return NULL.
602  *
603  * Note: interpretation of relative-path arguments during postmaster startup
604  * should happen before doing ChangeToDataDir(), else the user will probably
605  * not like the results.
606  */
607 char *
make_absolute_path(const char * path)608 make_absolute_path(const char *path)
609 {
610 	char	   *new;
611 
612 	/* Returning null for null input is convenient for some callers */
613 	if (path == NULL)
614 		return NULL;
615 
616 	if (!is_absolute_path(path))
617 	{
618 		char	   *buf;
619 		size_t		buflen;
620 
621 		buflen = MAXPGPATH;
622 		for (;;)
623 		{
624 			buf = malloc(buflen);
625 			if (!buf)
626 			{
627 #ifndef FRONTEND
628 				ereport(ERROR,
629 						(errcode(ERRCODE_OUT_OF_MEMORY),
630 						 errmsg("out of memory")));
631 #else
632 				fprintf(stderr, _("out of memory\n"));
633 				return NULL;
634 #endif
635 			}
636 
637 			if (getcwd(buf, buflen))
638 				break;
639 			else if (errno == ERANGE)
640 			{
641 				free(buf);
642 				buflen *= 2;
643 				continue;
644 			}
645 			else
646 			{
647 				int			save_errno = errno;
648 
649 				free(buf);
650 				errno = save_errno;
651 #ifndef FRONTEND
652 				elog(ERROR, "could not get current working directory: %m");
653 #else
654 				fprintf(stderr, _("could not get current working directory: %s\n"),
655 						strerror(errno));
656 				return NULL;
657 #endif
658 			}
659 		}
660 
661 		new = malloc(strlen(buf) + strlen(path) + 2);
662 		if (!new)
663 		{
664 			free(buf);
665 #ifndef FRONTEND
666 			ereport(ERROR,
667 					(errcode(ERRCODE_OUT_OF_MEMORY),
668 					 errmsg("out of memory")));
669 #else
670 			fprintf(stderr, _("out of memory\n"));
671 			return NULL;
672 #endif
673 		}
674 		sprintf(new, "%s/%s", buf, path);
675 		free(buf);
676 	}
677 	else
678 	{
679 		new = strdup(path);
680 		if (!new)
681 		{
682 #ifndef FRONTEND
683 			ereport(ERROR,
684 					(errcode(ERRCODE_OUT_OF_MEMORY),
685 					 errmsg("out of memory")));
686 #else
687 			fprintf(stderr, _("out of memory\n"));
688 			return NULL;
689 #endif
690 		}
691 	}
692 
693 	/* Make sure punctuation is canonical, too */
694 	canonicalize_path(new);
695 
696 	return new;
697 }
698 
699 
700 /*
701  *	get_share_path
702  */
703 void
get_share_path(const char * my_exec_path,char * ret_path)704 get_share_path(const char *my_exec_path, char *ret_path)
705 {
706 	make_relative_path(ret_path, PGSHAREDIR, PGBINDIR, my_exec_path);
707 }
708 
709 /*
710  *	get_etc_path
711  */
712 void
get_etc_path(const char * my_exec_path,char * ret_path)713 get_etc_path(const char *my_exec_path, char *ret_path)
714 {
715 	make_relative_path(ret_path, SYSCONFDIR, PGBINDIR, my_exec_path);
716 }
717 
718 /*
719  *	get_include_path
720  */
721 void
get_include_path(const char * my_exec_path,char * ret_path)722 get_include_path(const char *my_exec_path, char *ret_path)
723 {
724 	make_relative_path(ret_path, INCLUDEDIR, PGBINDIR, my_exec_path);
725 }
726 
727 /*
728  *	get_pkginclude_path
729  */
730 void
get_pkginclude_path(const char * my_exec_path,char * ret_path)731 get_pkginclude_path(const char *my_exec_path, char *ret_path)
732 {
733 	make_relative_path(ret_path, PKGINCLUDEDIR, PGBINDIR, my_exec_path);
734 }
735 
736 /*
737  *	get_includeserver_path
738  */
739 void
get_includeserver_path(const char * my_exec_path,char * ret_path)740 get_includeserver_path(const char *my_exec_path, char *ret_path)
741 {
742 	make_relative_path(ret_path, INCLUDEDIRSERVER, PGBINDIR, my_exec_path);
743 }
744 
745 /*
746  *	get_lib_path
747  */
748 void
get_lib_path(const char * my_exec_path,char * ret_path)749 get_lib_path(const char *my_exec_path, char *ret_path)
750 {
751 	make_relative_path(ret_path, LIBDIR, PGBINDIR, my_exec_path);
752 }
753 
754 /*
755  *	get_pkglib_path
756  */
757 void
get_pkglib_path(const char * my_exec_path,char * ret_path)758 get_pkglib_path(const char *my_exec_path, char *ret_path)
759 {
760 	make_relative_path(ret_path, PKGLIBDIR, PGBINDIR, my_exec_path);
761 }
762 
763 /*
764  *	get_locale_path
765  */
766 void
get_locale_path(const char * my_exec_path,char * ret_path)767 get_locale_path(const char *my_exec_path, char *ret_path)
768 {
769 	make_relative_path(ret_path, LOCALEDIR, PGBINDIR, my_exec_path);
770 }
771 
772 /*
773  *	get_doc_path
774  */
775 void
get_doc_path(const char * my_exec_path,char * ret_path)776 get_doc_path(const char *my_exec_path, char *ret_path)
777 {
778 	make_relative_path(ret_path, DOCDIR, PGBINDIR, my_exec_path);
779 }
780 
781 /*
782  *	get_html_path
783  */
784 void
get_html_path(const char * my_exec_path,char * ret_path)785 get_html_path(const char *my_exec_path, char *ret_path)
786 {
787 	make_relative_path(ret_path, HTMLDIR, PGBINDIR, my_exec_path);
788 }
789 
790 /*
791  *	get_man_path
792  */
793 void
get_man_path(const char * my_exec_path,char * ret_path)794 get_man_path(const char *my_exec_path, char *ret_path)
795 {
796 	make_relative_path(ret_path, MANDIR, PGBINDIR, my_exec_path);
797 }
798 
799 
800 /*
801  *	get_home_path
802  *
803  * On Unix, this actually returns the user's home directory.  On Windows
804  * it returns the PostgreSQL-specific application data folder.
805  */
806 bool
get_home_path(char * ret_path)807 get_home_path(char *ret_path)
808 {
809 #ifndef WIN32
810 	char		pwdbuf[BUFSIZ];
811 	struct passwd pwdstr;
812 	struct passwd *pwd = NULL;
813 
814 	(void) pqGetpwuid(geteuid(), &pwdstr, pwdbuf, sizeof(pwdbuf), &pwd);
815 	if (pwd == NULL)
816 		return false;
817 	strlcpy(ret_path, pwd->pw_dir, MAXPGPATH);
818 	return true;
819 #else
820 	char	   *tmppath;
821 
822 	/*
823 	 * Note: We use getenv() here because the more modern SHGetFolderPath()
824 	 * would force the backend to link with shell32.lib, which eats valuable
825 	 * desktop heap.  XXX This function is used only in psql, which already
826 	 * brings in shell32 via libpq.  Moving this function to its own file
827 	 * would keep it out of the backend, freeing it from this concern.
828 	 */
829 	tmppath = getenv("APPDATA");
830 	if (!tmppath)
831 		return false;
832 	snprintf(ret_path, MAXPGPATH, "%s/postgresql", tmppath);
833 	return true;
834 #endif
835 }
836 
837 
838 /*
839  * get_parent_directory
840  *
841  * Modify the given string in-place to name the parent directory of the
842  * named file.
843  *
844  * If the input is just a file name with no directory part, the result is
845  * an empty string, not ".".  This is appropriate when the next step is
846  * join_path_components(), but might need special handling otherwise.
847  *
848  * Caution: this will not produce desirable results if the string ends
849  * with "..".  For most callers this is not a problem since the string
850  * is already known to name a regular file.  If in doubt, apply
851  * canonicalize_path() first.
852  */
853 void
get_parent_directory(char * path)854 get_parent_directory(char *path)
855 {
856 	trim_directory(path);
857 }
858 
859 
860 /*
861  *	trim_directory
862  *
863  *	Trim trailing directory from path, that is, remove any trailing slashes,
864  *	the last pathname component, and the slash just ahead of it --- but never
865  *	remove a leading slash.
866  */
867 static void
trim_directory(char * path)868 trim_directory(char *path)
869 {
870 	char	   *p;
871 
872 	path = skip_drive(path);
873 
874 	if (path[0] == '\0')
875 		return;
876 
877 	/* back up over trailing slash(es) */
878 	for (p = path + strlen(path) - 1; IS_DIR_SEP(*p) && p > path; p--)
879 		;
880 	/* back up over directory name */
881 	for (; !IS_DIR_SEP(*p) && p > path; p--)
882 		;
883 	/* if multiple slashes before directory name, remove 'em all */
884 	for (; p > path && IS_DIR_SEP(*(p - 1)); p--)
885 		;
886 	/* don't erase a leading slash */
887 	if (p == path && IS_DIR_SEP(*p))
888 		p++;
889 	*p = '\0';
890 }
891 
892 
893 /*
894  *	trim_trailing_separator
895  *
896  * trim off trailing slashes, but not a leading slash
897  */
898 static void
trim_trailing_separator(char * path)899 trim_trailing_separator(char *path)
900 {
901 	char	   *p;
902 
903 	path = skip_drive(path);
904 	p = path + strlen(path);
905 	if (p > path)
906 		for (p--; p > path && IS_DIR_SEP(*p); p--)
907 			*p = '\0';
908 }
909