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