Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * path.c
4 : * portable path handling routines
5 : *
6 : * Portions Copyright (c) 1996-2024, 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 char *trim_directory(char *path);
50 : static void trim_trailing_separator(char *path);
51 : static char *append_subdir_to_path(char *path, char *subdir);
52 :
53 :
54 : /*
55 : * skip_drive
56 : *
57 : * On Windows, a path may begin with "C:" or "//network/". Advance over
58 : * this and point to the effective start of the path.
59 : */
60 : #ifdef WIN32
61 :
62 : static char *
63 : skip_drive(const char *path)
64 : {
65 : if (IS_DIR_SEP(path[0]) && IS_DIR_SEP(path[1]))
66 : {
67 : path += 2;
68 : while (*path && !IS_DIR_SEP(*path))
69 : path++;
70 : }
71 : else if (isalpha((unsigned char) path[0]) && path[1] == ':')
72 : {
73 : path += 2;
74 : }
75 : return (char *) path;
76 : }
77 : #else
78 :
79 : #define skip_drive(path) (path)
80 : #endif
81 :
82 : /*
83 : * has_drive_prefix
84 : *
85 : * Return true if the given pathname has a drive prefix.
86 : */
87 : bool
88 0 : has_drive_prefix(const char *path)
89 : {
90 : #ifdef WIN32
91 : return skip_drive(path) != path;
92 : #else
93 0 : return false;
94 : #endif
95 : }
96 :
97 : /*
98 : * first_dir_separator
99 : *
100 : * Find the location of the first directory separator, return
101 : * NULL if not found.
102 : */
103 : char *
104 74898 : first_dir_separator(const char *filename)
105 : {
106 : const char *p;
107 :
108 458274 : for (p = skip_drive(filename); *p; p++)
109 437368 : if (IS_DIR_SEP(*p))
110 53992 : return unconstify(char *, p);
111 20906 : return NULL;
112 : }
113 :
114 : /*
115 : * first_path_var_separator
116 : *
117 : * Find the location of the first path separator (i.e. ':' on
118 : * Unix, ';' on Windows), return NULL if not found.
119 : */
120 : char *
121 15974 : first_path_var_separator(const char *pathlist)
122 : {
123 : const char *p;
124 :
125 : /* skip_drive is not needed */
126 532630 : for (p = pathlist; *p; p++)
127 524594 : if (IS_PATH_VAR_SEP(*p))
128 7938 : return unconstify(char *, p);
129 8036 : return NULL;
130 : }
131 :
132 : /*
133 : * last_dir_separator
134 : *
135 : * Find the location of the last directory separator, return
136 : * NULL if not found.
137 : */
138 : char *
139 309310 : last_dir_separator(const char *filename)
140 : {
141 : const char *p,
142 309310 : *ret = NULL;
143 :
144 6282644 : for (p = skip_drive(filename); *p; p++)
145 5973334 : if (IS_DIR_SEP(*p))
146 879734 : ret = p;
147 309310 : return unconstify(char *, ret);
148 : }
149 :
150 :
151 : /*
152 : * make_native_path - on WIN32, change / to \ in the path
153 : *
154 : * This effectively undoes canonicalize_path.
155 : *
156 : * This is required because WIN32 COPY is an internal CMD.EXE
157 : * command and doesn't process forward slashes in the same way
158 : * as external commands. Quoting the first argument to COPY
159 : * does not convert forward to backward slashes, but COPY does
160 : * properly process quoted forward slashes in the second argument.
161 : *
162 : * COPY works with quoted forward slashes in the first argument
163 : * only if the current directory is the same as the directory
164 : * of the first argument.
165 : */
166 : void
167 452 : make_native_path(char *filename)
168 : {
169 : #ifdef WIN32
170 : char *p;
171 :
172 : for (p = filename; *p; p++)
173 : if (*p == '/')
174 : *p = '\\';
175 : #endif
176 452 : }
177 :
178 :
179 : /*
180 : * This function cleans up the paths for use with either cmd.exe or Msys
181 : * on Windows. We need them to use filenames without spaces, for which a
182 : * short filename is the safest equivalent, eg:
183 : * C:/Progra~1/
184 : */
185 : void
186 10114 : cleanup_path(char *path)
187 : {
188 : #ifdef WIN32
189 : char *ptr;
190 :
191 : /*
192 : * GetShortPathName() will fail if the path does not exist, or short names
193 : * are disabled on this file system. In both cases, we just return the
194 : * original path. This is particularly useful for --sysconfdir, which
195 : * might not exist.
196 : */
197 : GetShortPathName(path, path, MAXPGPATH - 1);
198 :
199 : /* Replace '\' with '/' */
200 : for (ptr = path; *ptr; ptr++)
201 : {
202 : if (*ptr == '\\')
203 : *ptr = '/';
204 : }
205 : #endif
206 10114 : }
207 :
208 :
209 : /*
210 : * join_path_components - join two path components, inserting a slash
211 : *
212 : * We omit the slash if either given component is empty.
213 : *
214 : * ret_path is the output area (must be of size MAXPGPATH)
215 : *
216 : * ret_path can be the same as head, but not the same as tail.
217 : */
218 : void
219 89404 : join_path_components(char *ret_path,
220 : const char *head, const char *tail)
221 : {
222 89404 : if (ret_path != head)
223 2596 : strlcpy(ret_path, head, MAXPGPATH);
224 :
225 : /*
226 : * We used to try to simplify some cases involving "." and "..", but now
227 : * we just leave that to be done by canonicalize_path() later.
228 : */
229 :
230 89404 : if (*tail)
231 : {
232 : /* only separate with slash if head wasn't empty */
233 89404 : snprintf(ret_path + strlen(ret_path), MAXPGPATH - strlen(ret_path),
234 : "%s%s",
235 89404 : (*(skip_drive(head)) != '\0') ? "/" : "",
236 : tail);
237 : }
238 89404 : }
239 :
240 :
241 : /* State-machine states for canonicalize_path */
242 : typedef enum
243 : {
244 : ABSOLUTE_PATH_INIT, /* Just past the leading '/' (and Windows
245 : * drive name if any) of an absolute path */
246 : ABSOLUTE_WITH_N_DEPTH, /* We collected 'pathdepth' directories in an
247 : * absolute path */
248 : RELATIVE_PATH_INIT, /* At start of a relative path */
249 : RELATIVE_WITH_N_DEPTH, /* We collected 'pathdepth' directories in a
250 : * relative path */
251 : RELATIVE_WITH_PARENT_REF, /* Relative path containing only double-dots */
252 : } canonicalize_state;
253 :
254 : /*
255 : * Clean up path by:
256 : * o make Win32 path use Unix slashes
257 : * o remove trailing quote on Win32
258 : * o remove trailing slash
259 : * o remove duplicate (adjacent) separators
260 : * o remove '.' (unless path reduces to only '.')
261 : * o process '..' ourselves, removing it if possible
262 : */
263 : void
264 215046 : canonicalize_path(char *path)
265 : {
266 : char *p,
267 : *to_p;
268 : char *spath;
269 : char *parsed;
270 : char *unparse;
271 215046 : bool was_sep = false;
272 : canonicalize_state state;
273 215046 : int pathdepth = 0; /* counts collected regular directory names */
274 :
275 : #ifdef WIN32
276 :
277 : /*
278 : * The Windows command processor will accept suitably quoted paths with
279 : * forward slashes, but barfs badly with mixed forward and back slashes.
280 : */
281 : for (p = path; *p; p++)
282 : {
283 : if (*p == '\\')
284 : *p = '/';
285 : }
286 :
287 : /*
288 : * In Win32, if you do: prog.exe "a b" "\c\d\" the system will pass \c\d"
289 : * as argv[2], so trim off trailing quote.
290 : */
291 : if (p > path && *(p - 1) == '"')
292 : *(p - 1) = '/';
293 : #endif
294 :
295 : /*
296 : * Removing the trailing slash on a path means we never get ugly double
297 : * trailing slashes. Also, Win32 can't stat() a directory with a trailing
298 : * slash. Don't remove a leading slash, though.
299 : */
300 215046 : trim_trailing_separator(path);
301 :
302 : /*
303 : * Remove duplicate adjacent separators
304 : */
305 215046 : p = path;
306 : #ifdef WIN32
307 : /* Don't remove leading double-slash on Win32 */
308 : if (*p)
309 : p++;
310 : #endif
311 215046 : to_p = p;
312 12425812 : for (; *p; p++, to_p++)
313 : {
314 : /* Handle many adjacent slashes, like "/a///b" */
315 12210766 : while (*p == '/' && was_sep)
316 0 : p++;
317 12210766 : if (to_p != p)
318 0 : *to_p = *p;
319 12210766 : was_sep = (*p == '/');
320 : }
321 215046 : *to_p = '\0';
322 :
323 : /*
324 : * Remove any uses of "." and process ".." ourselves
325 : *
326 : * Note that "/../.." should reduce to just "/", while "../.." has to be
327 : * kept as-is. Also note that we want a Windows drive spec to be visible
328 : * to trim_directory(), but it's not part of the logic that's looking at
329 : * the name components; hence distinction between path and spath.
330 : *
331 : * This loop overwrites the path in-place. This is safe since we'll never
332 : * make the path longer. "unparse" points to where we are reading the
333 : * path, "parse" to where we are writing.
334 : */
335 215046 : spath = skip_drive(path);
336 215046 : if (*spath == '\0')
337 66 : return; /* empty path is returned as-is */
338 :
339 214980 : if (*spath == '/')
340 : {
341 193568 : state = ABSOLUTE_PATH_INIT;
342 : /* Skip the leading slash for absolute path */
343 193568 : parsed = unparse = (spath + 1);
344 : }
345 : else
346 : {
347 21412 : state = RELATIVE_PATH_INIT;
348 21412 : parsed = unparse = spath;
349 : }
350 :
351 2043910 : while (*unparse != '\0')
352 : {
353 : char *unparse_next;
354 : bool is_double_dot;
355 :
356 : /* Split off this dir name, and set unparse_next to the next one */
357 1828930 : unparse_next = unparse;
358 12232172 : while (*unparse_next && *unparse_next != '/')
359 10403242 : unparse_next++;
360 1828930 : if (*unparse_next != '\0')
361 1613956 : *unparse_next++ = '\0';
362 :
363 : /* Identify type of this dir name */
364 1828930 : if (strcmp(unparse, ".") == 0)
365 : {
366 : /* We can ignore "." components in all cases */
367 802 : unparse = unparse_next;
368 802 : continue;
369 : }
370 :
371 1828128 : if (strcmp(unparse, "..") == 0)
372 230 : is_double_dot = true;
373 : else
374 : {
375 : /* adjacent separators were eliminated above */
376 : Assert(*unparse != '\0');
377 1827898 : is_double_dot = false;
378 : }
379 :
380 1828128 : switch (state)
381 : {
382 193616 : case ABSOLUTE_PATH_INIT:
383 : /* We can ignore ".." immediately after / */
384 193616 : if (!is_double_dot)
385 : {
386 : /* Append first dir name (we already have leading slash) */
387 193580 : parsed = append_subdir_to_path(parsed, unparse);
388 193580 : state = ABSOLUTE_WITH_N_DEPTH;
389 193580 : pathdepth++;
390 : }
391 193616 : break;
392 1577624 : case ABSOLUTE_WITH_N_DEPTH:
393 1577624 : if (is_double_dot)
394 : {
395 : /* Remove last parsed dir */
396 : /* (trim_directory won't remove the leading slash) */
397 98 : *parsed = '\0';
398 98 : parsed = trim_directory(path);
399 98 : if (--pathdepth == 0)
400 30 : state = ABSOLUTE_PATH_INIT;
401 : }
402 : else
403 : {
404 : /* Append normal dir */
405 1577526 : *parsed++ = '/';
406 1577526 : parsed = append_subdir_to_path(parsed, unparse);
407 1577526 : pathdepth++;
408 : }
409 1577624 : break;
410 21376 : case RELATIVE_PATH_INIT:
411 21376 : if (is_double_dot)
412 : {
413 : /* Append irreducible double-dot (..) */
414 36 : parsed = append_subdir_to_path(parsed, unparse);
415 36 : state = RELATIVE_WITH_PARENT_REF;
416 : }
417 : else
418 : {
419 : /* Append normal dir */
420 21340 : parsed = append_subdir_to_path(parsed, unparse);
421 21340 : state = RELATIVE_WITH_N_DEPTH;
422 21340 : pathdepth++;
423 : }
424 21376 : break;
425 35464 : case RELATIVE_WITH_N_DEPTH:
426 35464 : if (is_double_dot)
427 : {
428 : /* Remove last parsed dir */
429 54 : *parsed = '\0';
430 54 : parsed = trim_directory(path);
431 54 : if (--pathdepth == 0)
432 : {
433 : /*
434 : * If the output path is now empty, we're back to the
435 : * INIT state. However, we could have processed a
436 : * path like "../dir/.." and now be down to "..", in
437 : * which case enter the correct state for that.
438 : */
439 42 : if (parsed == spath)
440 24 : state = RELATIVE_PATH_INIT;
441 : else
442 18 : state = RELATIVE_WITH_PARENT_REF;
443 : }
444 : }
445 : else
446 : {
447 : /* Append normal dir */
448 35410 : *parsed++ = '/';
449 35410 : parsed = append_subdir_to_path(parsed, unparse);
450 35410 : pathdepth++;
451 : }
452 35464 : break;
453 48 : case RELATIVE_WITH_PARENT_REF:
454 48 : if (is_double_dot)
455 : {
456 : /* Append next irreducible double-dot (..) */
457 6 : *parsed++ = '/';
458 6 : parsed = append_subdir_to_path(parsed, unparse);
459 : }
460 : else
461 : {
462 : /* Append normal dir */
463 42 : *parsed++ = '/';
464 42 : parsed = append_subdir_to_path(parsed, unparse);
465 :
466 : /*
467 : * We can now start counting normal dirs. But if later
468 : * double-dots make us remove this dir again, we'd better
469 : * revert to RELATIVE_WITH_PARENT_REF not INIT state.
470 : */
471 42 : state = RELATIVE_WITH_N_DEPTH;
472 42 : pathdepth = 1;
473 : }
474 48 : break;
475 : }
476 :
477 1828128 : unparse = unparse_next;
478 : }
479 :
480 : /*
481 : * If our output path is empty at this point, insert ".". We don't want
482 : * to do this any earlier because it'd result in an extra dot in corner
483 : * cases such as "../dir/..". Since we rejected the wholly-empty-path
484 : * case above, there is certainly room.
485 : */
486 214980 : if (parsed == spath)
487 60 : *parsed++ = '.';
488 :
489 : /* And finally, ensure the output path is nul-terminated. */
490 214980 : *parsed = '\0';
491 : }
492 :
493 : /*
494 : * Detect whether a path contains any parent-directory references ("..")
495 : *
496 : * The input *must* have been put through canonicalize_path previously.
497 : */
498 : bool
499 13798 : path_contains_parent_reference(const char *path)
500 : {
501 : /*
502 : * Once canonicalized, an absolute path cannot contain any ".." at all,
503 : * while a relative path could contain ".."(s) only at the start. So it
504 : * is sufficient to check the start of the path, after skipping any
505 : * Windows drive/network specifier.
506 : */
507 13798 : path = skip_drive(path); /* C: shouldn't affect our conclusion */
508 :
509 13798 : if (path[0] == '.' &&
510 8 : path[1] == '.' &&
511 0 : (path[2] == '\0' || path[2] == '/'))
512 0 : return true;
513 :
514 13798 : return false;
515 : }
516 :
517 : /*
518 : * Detect whether a path is only in or below the current working directory.
519 : *
520 : * The input *must* have been put through canonicalize_path previously.
521 : *
522 : * An absolute path that matches the current working directory should
523 : * return false (we only want relative to the cwd).
524 : */
525 : bool
526 13798 : path_is_relative_and_below_cwd(const char *path)
527 : {
528 13798 : if (is_absolute_path(path))
529 0 : return false;
530 : /* don't allow anything above the cwd */
531 13798 : else if (path_contains_parent_reference(path))
532 0 : return false;
533 : #ifdef WIN32
534 :
535 : /*
536 : * On Win32, a drive letter _not_ followed by a slash, e.g. 'E:abc', is
537 : * relative to the cwd on that drive, or the drive's root directory if
538 : * that drive has no cwd. Because the path itself cannot tell us which is
539 : * the case, we have to assume the worst, i.e. that it is not below the
540 : * cwd. We could use GetFullPathName() to find the full path but that
541 : * could change if the current directory for the drive changes underneath
542 : * us, so we just disallow it.
543 : */
544 : else if (isalpha((unsigned char) path[0]) && path[1] == ':' &&
545 : !IS_DIR_SEP(path[2]))
546 : return false;
547 : #endif
548 : else
549 13798 : return true;
550 : }
551 :
552 : /*
553 : * Detect whether path1 is a prefix of path2 (including equality).
554 : *
555 : * This is pretty trivial, but it seems better to export a function than
556 : * to export IS_DIR_SEP.
557 : */
558 : bool
559 108 : path_is_prefix_of_path(const char *path1, const char *path2)
560 : {
561 108 : int path1_len = strlen(path1);
562 :
563 108 : if (strncmp(path1, path2, path1_len) == 0 &&
564 0 : (IS_DIR_SEP(path2[path1_len]) || path2[path1_len] == '\0'))
565 0 : return true;
566 108 : return false;
567 : }
568 :
569 : /*
570 : * Extracts the actual name of the program as called -
571 : * stripped of .exe suffix if any
572 : */
573 : const char *
574 48110 : get_progname(const char *argv0)
575 : {
576 : const char *nodir_name;
577 : char *progname;
578 :
579 48110 : nodir_name = last_dir_separator(argv0);
580 48110 : if (nodir_name)
581 36920 : nodir_name++;
582 : else
583 11190 : nodir_name = skip_drive(argv0);
584 :
585 : /*
586 : * Make a copy in case argv[0] is modified by ps_status. Leaks memory, but
587 : * called only once.
588 : */
589 48110 : progname = strdup(nodir_name);
590 48110 : if (progname == NULL)
591 : {
592 0 : fprintf(stderr, "%s: out of memory\n", nodir_name);
593 0 : abort(); /* This could exit the postmaster */
594 : }
595 :
596 : #if defined(__CYGWIN__) || defined(WIN32)
597 : /* strip ".exe" suffix, regardless of case */
598 : if (strlen(progname) > sizeof(EXE) - 1 &&
599 : pg_strcasecmp(progname + strlen(progname) - (sizeof(EXE) - 1), EXE) == 0)
600 : progname[strlen(progname) - (sizeof(EXE) - 1)] = '\0';
601 : #endif
602 :
603 48110 : return progname;
604 : }
605 :
606 :
607 : /*
608 : * dir_strcmp: strcmp except any two DIR_SEP characters are considered equal,
609 : * and we honor filesystem case insensitivity if known
610 : */
611 : static int
612 78642 : dir_strcmp(const char *s1, const char *s2)
613 : {
614 314568 : while (*s1 && *s2)
615 : {
616 235926 : if (
617 : #ifndef WIN32
618 235926 : *s1 != *s2
619 : #else
620 : /* On windows, paths are case-insensitive */
621 : pg_tolower((unsigned char) *s1) != pg_tolower((unsigned char) *s2)
622 : #endif
623 0 : && !(IS_DIR_SEP(*s1) && IS_DIR_SEP(*s2)))
624 0 : return (int) *s1 - (int) *s2;
625 235926 : s1++, s2++;
626 : }
627 78642 : if (*s1)
628 0 : return 1; /* s1 longer */
629 78642 : if (*s2)
630 0 : return -1; /* s2 longer */
631 78642 : return 0;
632 : }
633 :
634 :
635 : /*
636 : * make_relative_path - make a path relative to the actual binary location
637 : *
638 : * This function exists to support relocation of installation trees.
639 : *
640 : * ret_path is the output area (must be of size MAXPGPATH)
641 : * target_path is the compiled-in path to the directory we want to find
642 : * bin_path is the compiled-in path to the directory of executables
643 : * my_exec_path is the actual location of my executable
644 : *
645 : * We determine the common prefix of target_path and bin_path, then compare
646 : * the remainder of bin_path to the last directory component(s) of
647 : * my_exec_path. If they match, build the result as the part of my_exec_path
648 : * preceding the match, joined to the remainder of target_path. If no match,
649 : * return target_path as-is.
650 : *
651 : * For example:
652 : * target_path = '/usr/local/share/postgresql'
653 : * bin_path = '/usr/local/bin'
654 : * my_exec_path = '/opt/pgsql/bin/postgres'
655 : * Given these inputs, the common prefix is '/usr/local/', the tail of
656 : * bin_path is 'bin' which does match the last directory component of
657 : * my_exec_path, so we would return '/opt/pgsql/share/postgresql'
658 : */
659 : static void
660 80418 : make_relative_path(char *ret_path, const char *target_path,
661 : const char *bin_path, const char *my_exec_path)
662 : {
663 : int prefix_len;
664 : int tail_start;
665 : int tail_len;
666 : int i;
667 :
668 : /*
669 : * Determine the common prefix --- note we require it to end on a
670 : * directory separator, consider eg '/usr/lib' and '/usr/libexec'.
671 : */
672 80418 : prefix_len = 0;
673 1447524 : for (i = 0; target_path[i] && bin_path[i]; i++)
674 : {
675 1447524 : if (IS_DIR_SEP(target_path[i]) && IS_DIR_SEP(bin_path[i]))
676 321672 : prefix_len = i + 1;
677 1125852 : else if (target_path[i] != bin_path[i])
678 80418 : break;
679 : }
680 80418 : if (prefix_len == 0)
681 0 : goto no_match; /* no common prefix? */
682 80418 : tail_len = strlen(bin_path) - prefix_len;
683 :
684 : /*
685 : * Set up my_exec_path without the actual executable name, and
686 : * canonicalize to simplify comparison to bin_path.
687 : */
688 80418 : strlcpy(ret_path, my_exec_path, MAXPGPATH);
689 80418 : trim_directory(ret_path); /* remove my executable name */
690 80418 : canonicalize_path(ret_path);
691 :
692 : /*
693 : * Tail match?
694 : */
695 80418 : tail_start = (int) strlen(ret_path) - tail_len;
696 80418 : if (tail_start > 0 &&
697 159060 : IS_DIR_SEP(ret_path[tail_start - 1]) &&
698 78642 : dir_strcmp(ret_path + tail_start, bin_path + prefix_len) == 0)
699 : {
700 78642 : ret_path[tail_start] = '\0';
701 78642 : trim_trailing_separator(ret_path);
702 78642 : join_path_components(ret_path, ret_path, target_path + prefix_len);
703 78642 : canonicalize_path(ret_path);
704 78642 : return;
705 : }
706 :
707 1776 : no_match:
708 1776 : strlcpy(ret_path, target_path, MAXPGPATH);
709 1776 : canonicalize_path(ret_path);
710 : }
711 :
712 :
713 : /*
714 : * make_absolute_path
715 : *
716 : * If the given pathname isn't already absolute, make it so, interpreting
717 : * it relative to the current working directory.
718 : *
719 : * Also canonicalizes the path. The result is always a malloc'd copy.
720 : *
721 : * In backend, failure cases result in ereport(ERROR); in frontend,
722 : * we write a complaint on stderr and return NULL.
723 : *
724 : * Note: interpretation of relative-path arguments during postmaster startup
725 : * should happen before doing ChangeToDataDir(), else the user will probably
726 : * not like the results.
727 : */
728 : char *
729 4434 : make_absolute_path(const char *path)
730 : {
731 : char *new;
732 :
733 : /* Returning null for null input is convenient for some callers */
734 4434 : if (path == NULL)
735 0 : return NULL;
736 :
737 4434 : if (!is_absolute_path(path))
738 : {
739 : char *buf;
740 : size_t buflen;
741 :
742 674 : buflen = MAXPGPATH;
743 : for (;;)
744 : {
745 674 : buf = malloc(buflen);
746 674 : if (!buf)
747 : {
748 : #ifndef FRONTEND
749 0 : ereport(ERROR,
750 : (errcode(ERRCODE_OUT_OF_MEMORY),
751 : errmsg("out of memory")));
752 : #else
753 0 : fprintf(stderr, _("out of memory\n"));
754 0 : return NULL;
755 : #endif
756 : }
757 :
758 674 : if (getcwd(buf, buflen))
759 674 : break;
760 0 : else if (errno == ERANGE)
761 : {
762 0 : free(buf);
763 0 : buflen *= 2;
764 0 : continue;
765 : }
766 : else
767 : {
768 0 : int save_errno = errno;
769 :
770 0 : free(buf);
771 0 : errno = save_errno;
772 : #ifndef FRONTEND
773 0 : elog(ERROR, "could not get current working directory: %m");
774 : #else
775 0 : fprintf(stderr, _("could not get current working directory: %m\n"));
776 0 : return NULL;
777 : #endif
778 : }
779 : }
780 :
781 674 : new = malloc(strlen(buf) + strlen(path) + 2);
782 674 : if (!new)
783 : {
784 0 : free(buf);
785 : #ifndef FRONTEND
786 0 : ereport(ERROR,
787 : (errcode(ERRCODE_OUT_OF_MEMORY),
788 : errmsg("out of memory")));
789 : #else
790 0 : fprintf(stderr, _("out of memory\n"));
791 0 : return NULL;
792 : #endif
793 : }
794 674 : sprintf(new, "%s/%s", buf, path);
795 674 : free(buf);
796 : }
797 : else
798 : {
799 3760 : new = strdup(path);
800 3760 : if (!new)
801 : {
802 : #ifndef FRONTEND
803 0 : ereport(ERROR,
804 : (errcode(ERRCODE_OUT_OF_MEMORY),
805 : errmsg("out of memory")));
806 : #else
807 0 : fprintf(stderr, _("out of memory\n"));
808 0 : return NULL;
809 : #endif
810 : }
811 : }
812 :
813 : /* Make sure punctuation is canonical, too */
814 4434 : canonicalize_path(new);
815 :
816 4434 : return new;
817 : }
818 :
819 :
820 : /*
821 : * get_share_path
822 : */
823 : void
824 22356 : get_share_path(const char *my_exec_path, char *ret_path)
825 : {
826 22356 : make_relative_path(ret_path, PGSHAREDIR, PGBINDIR, my_exec_path);
827 22356 : }
828 :
829 : /*
830 : * get_etc_path
831 : */
832 : void
833 19602 : get_etc_path(const char *my_exec_path, char *ret_path)
834 : {
835 19602 : make_relative_path(ret_path, SYSCONFDIR, PGBINDIR, my_exec_path);
836 19602 : }
837 :
838 : /*
839 : * get_include_path
840 : */
841 : void
842 910 : get_include_path(const char *my_exec_path, char *ret_path)
843 : {
844 910 : make_relative_path(ret_path, INCLUDEDIR, PGBINDIR, my_exec_path);
845 910 : }
846 :
847 : /*
848 : * get_pkginclude_path
849 : */
850 : void
851 798 : get_pkginclude_path(const char *my_exec_path, char *ret_path)
852 : {
853 798 : make_relative_path(ret_path, PKGINCLUDEDIR, PGBINDIR, my_exec_path);
854 798 : }
855 :
856 : /*
857 : * get_includeserver_path
858 : */
859 : void
860 778 : get_includeserver_path(const char *my_exec_path, char *ret_path)
861 : {
862 778 : make_relative_path(ret_path, INCLUDEDIRSERVER, PGBINDIR, my_exec_path);
863 778 : }
864 :
865 : /*
866 : * get_lib_path
867 : */
868 : void
869 778 : get_lib_path(const char *my_exec_path, char *ret_path)
870 : {
871 778 : make_relative_path(ret_path, LIBDIR, PGBINDIR, my_exec_path);
872 778 : }
873 :
874 : /*
875 : * get_pkglib_path
876 : */
877 : void
878 3386 : get_pkglib_path(const char *my_exec_path, char *ret_path)
879 : {
880 3386 : make_relative_path(ret_path, PKGLIBDIR, PGBINDIR, my_exec_path);
881 3386 : }
882 :
883 : /*
884 : * get_locale_path
885 : */
886 : void
887 29476 : get_locale_path(const char *my_exec_path, char *ret_path)
888 : {
889 29476 : make_relative_path(ret_path, LOCALEDIR, PGBINDIR, my_exec_path);
890 29476 : }
891 :
892 : /*
893 : * get_doc_path
894 : */
895 : void
896 778 : get_doc_path(const char *my_exec_path, char *ret_path)
897 : {
898 778 : make_relative_path(ret_path, DOCDIR, PGBINDIR, my_exec_path);
899 778 : }
900 :
901 : /*
902 : * get_html_path
903 : */
904 : void
905 778 : get_html_path(const char *my_exec_path, char *ret_path)
906 : {
907 778 : make_relative_path(ret_path, HTMLDIR, PGBINDIR, my_exec_path);
908 778 : }
909 :
910 : /*
911 : * get_man_path
912 : */
913 : void
914 778 : get_man_path(const char *my_exec_path, char *ret_path)
915 : {
916 778 : make_relative_path(ret_path, MANDIR, PGBINDIR, my_exec_path);
917 778 : }
918 :
919 :
920 : /*
921 : * get_home_path
922 : *
923 : * On Unix, this actually returns the user's home directory. On Windows
924 : * it returns the PostgreSQL-specific application data folder.
925 : */
926 : bool
927 0 : get_home_path(char *ret_path)
928 : {
929 : #ifndef WIN32
930 : /*
931 : * We first consult $HOME. If that's unset, try to get the info from
932 : * <pwd.h>.
933 : */
934 : const char *home;
935 :
936 0 : home = getenv("HOME");
937 0 : if (home == NULL || home[0] == '\0')
938 0 : return pg_get_user_home_dir(geteuid(), ret_path, MAXPGPATH);
939 0 : strlcpy(ret_path, home, MAXPGPATH);
940 0 : return true;
941 : #else
942 : char *tmppath;
943 :
944 : /*
945 : * Note: We use getenv() here because the more modern SHGetFolderPath()
946 : * would force the backend to link with shell32.lib, which eats valuable
947 : * desktop heap. XXX This function is used only in psql, which already
948 : * brings in shell32 via libpq. Moving this function to its own file
949 : * would keep it out of the backend, freeing it from this concern.
950 : */
951 : tmppath = getenv("APPDATA");
952 : if (!tmppath)
953 : return false;
954 : snprintf(ret_path, MAXPGPATH, "%s/postgresql", tmppath);
955 : return true;
956 : #endif
957 : }
958 :
959 :
960 : /*
961 : * get_parent_directory
962 : *
963 : * Modify the given string in-place to name the parent directory of the
964 : * named file.
965 : *
966 : * If the input is just a file name with no directory part, the result is
967 : * an empty string, not ".". This is appropriate when the next step is
968 : * join_path_components(), but might need special handling otherwise.
969 : *
970 : * Caution: this will not produce desirable results if the string ends
971 : * with "..". For most callers this is not a problem since the string
972 : * is already known to name a regular file. If in doubt, apply
973 : * canonicalize_path() first.
974 : */
975 : void
976 5770 : get_parent_directory(char *path)
977 : {
978 5770 : trim_directory(path);
979 5770 : }
980 :
981 :
982 : /*
983 : * trim_directory
984 : *
985 : * Trim trailing directory from path, that is, remove any trailing slashes,
986 : * the last pathname component, and the slash just ahead of it --- but never
987 : * remove a leading slash.
988 : *
989 : * For the convenience of canonicalize_path, the path's new end location
990 : * is returned.
991 : */
992 : static char *
993 86340 : trim_directory(char *path)
994 : {
995 : char *p;
996 :
997 86340 : path = skip_drive(path);
998 :
999 86340 : if (path[0] == '\0')
1000 0 : return path;
1001 :
1002 : /* back up over trailing slash(es) */
1003 86340 : for (p = path + strlen(path) - 1; IS_DIR_SEP(*p) && p > path; p--)
1004 : ;
1005 : /* back up over directory name */
1006 754564 : for (; !IS_DIR_SEP(*p) && p > path; p--)
1007 : ;
1008 : /* if multiple slashes before directory name, remove 'em all */
1009 86340 : for (; p > path && IS_DIR_SEP(*(p - 1)); p--)
1010 : ;
1011 : /* don't erase a leading slash */
1012 86340 : if (p == path && IS_DIR_SEP(*p))
1013 30 : p++;
1014 86340 : *p = '\0';
1015 86340 : return p;
1016 : }
1017 :
1018 :
1019 : /*
1020 : * trim_trailing_separator
1021 : *
1022 : * trim off trailing slashes, but not a leading slash
1023 : */
1024 : static void
1025 293688 : trim_trailing_separator(char *path)
1026 : {
1027 : char *p;
1028 :
1029 293688 : path = skip_drive(path);
1030 293688 : p = path + strlen(path);
1031 293688 : if (p > path)
1032 373858 : for (p--; p > path && IS_DIR_SEP(*p); p--)
1033 80236 : *p = '\0';
1034 293688 : }
1035 :
1036 : /*
1037 : * append_subdir_to_path
1038 : *
1039 : * Append the currently-considered subdirectory name to the output
1040 : * path in canonicalize_path. Return the new end location of the
1041 : * output path.
1042 : *
1043 : * Since canonicalize_path updates the path in-place, we must use
1044 : * memmove not memcpy, and we don't yet terminate the path with '\0'.
1045 : */
1046 : static char *
1047 1827940 : append_subdir_to_path(char *path, char *subdir)
1048 : {
1049 1827940 : size_t len = strlen(subdir);
1050 :
1051 : /* No need to copy data if path and subdir are the same. */
1052 1827940 : if (path != subdir)
1053 410 : memmove(path, subdir, len);
1054 :
1055 1827940 : return path + len;
1056 : }
|