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