TLA Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * path.c
4 : * portable path handling routines
5 : *
6 : * Portions Copyright (c) 1996-2023, 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 UBC 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 CBC 44380 : first_dir_separator(const char *filename)
105 : {
106 : const char *p;
107 :
108 304283 : for (p = skip_drive(filename); *p; p++)
109 290226 : if (IS_DIR_SEP(*p))
110 30323 : return unconstify(char *, p);
111 14057 : 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 7823 : first_path_var_separator(const char *pathlist)
122 : {
123 : const char *p;
124 :
125 : /* skip_drive is not needed */
126 299139 : for (p = pathlist; *p; p++)
127 295617 : if (IS_PATH_VAR_SEP(*p))
128 4301 : return unconstify(char *, p);
129 3522 : 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 180230 : last_dir_separator(const char *filename)
140 : {
141 : const char *p,
142 180230 : *ret = NULL;
143 :
144 3434275 : for (p = skip_drive(filename); *p; p++)
145 3254045 : if (IS_DIR_SEP(*p))
146 480071 : ret = p;
147 180230 : 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 458 : 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 458 : }
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 4316 : 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 4316 : }
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 46787 : join_path_components(char *ret_path,
220 : const char *head, const char *tail)
221 : {
222 46787 : if (ret_path != head)
223 2182 : 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 46787 : if (*tail)
231 : {
232 : /* only separate with slash if head wasn't empty */
233 46787 : snprintf(ret_path + strlen(ret_path), MAXPGPATH - strlen(ret_path),
234 : "%s%s",
235 46787 : (*(skip_drive(head)) != '\0') ? "/" : "",
236 : tail);
237 : }
238 46787 : }
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 112554 : canonicalize_path(char *path)
265 : {
266 : char *p,
267 : *to_p;
268 : char *spath;
269 : char *parsed;
270 : char *unparse;
271 112554 : bool was_sep = false;
272 : canonicalize_state state;
273 112554 : 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 112554 : trim_trailing_separator(path);
301 :
302 : /*
303 : * Remove duplicate adjacent separators
304 : */
305 112554 : p = path;
306 : #ifdef WIN32
307 : /* Don't remove leading double-slash on Win32 */
308 : if (*p)
309 : p++;
310 : #endif
311 112554 : to_p = p;
312 7180307 : for (; *p; p++, to_p++)
313 : {
314 : /* Handle many adjacent slashes, like "/a///b" */
315 7068417 : while (*p == '/' && was_sep)
316 664 : p++;
317 7067753 : if (to_p != p)
318 6640 : *to_p = *p;
319 7067753 : was_sep = (*p == '/');
320 : }
321 112554 : *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 112554 : spath = skip_drive(path);
336 112554 : if (*spath == '\0')
337 28 : return; /* empty path is returned as-is */
338 :
339 112526 : if (*spath == '/')
340 : {
341 100628 : state = ABSOLUTE_PATH_INIT;
342 : /* Skip the leading slash for absolute path */
343 100628 : parsed = unparse = (spath + 1);
344 : }
345 : else
346 : {
347 11898 : state = RELATIVE_PATH_INIT;
348 11898 : parsed = unparse = spath;
349 : }
350 :
351 996541 : 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 884015 : unparse_next = unparse;
358 7079648 : while (*unparse_next && *unparse_next != '/')
359 6195633 : unparse_next++;
360 884015 : if (*unparse_next != '\0')
361 771492 : *unparse_next++ = '\0';
362 :
363 : /* Identify type of this dir name */
364 884015 : if (strcmp(unparse, ".") == 0)
365 : {
366 : /* We can ignore "." components in all cases */
367 314 : unparse = unparse_next;
368 314 : continue;
369 : }
370 :
371 883701 : if (strcmp(unparse, "..") == 0)
372 117 : is_double_dot = true;
373 : else
374 : {
375 : /* adjacent separators were eliminated above */
376 883584 : Assert(*unparse != '\0');
377 883584 : is_double_dot = false;
378 : }
379 :
380 883701 : switch (state)
381 : {
382 100652 : case ABSOLUTE_PATH_INIT:
383 : /* We can ignore ".." immediately after / */
384 100652 : if (!is_double_dot)
385 : {
386 : /* Append first dir name (we already have leading slash) */
387 100634 : parsed = append_subdir_to_path(parsed, unparse);
388 100634 : state = ABSOLUTE_WITH_N_DEPTH;
389 100634 : pathdepth++;
390 : }
391 100652 : break;
392 753478 : case ABSOLUTE_WITH_N_DEPTH:
393 753478 : if (is_double_dot)
394 : {
395 : /* Remove last parsed dir */
396 : /* (trim_directory won't remove the leading slash) */
397 50 : *parsed = '\0';
398 50 : parsed = trim_directory(path);
399 50 : if (--pathdepth == 0)
400 15 : state = ABSOLUTE_PATH_INIT;
401 : }
402 : else
403 : {
404 : /* Append normal dir */
405 753428 : *parsed++ = '/';
406 753428 : parsed = append_subdir_to_path(parsed, unparse);
407 753428 : pathdepth++;
408 : }
409 753478 : break;
410 11880 : case RELATIVE_PATH_INIT:
411 11880 : if (is_double_dot)
412 : {
413 : /* Append irreducible double-dot (..) */
414 19 : parsed = append_subdir_to_path(parsed, unparse);
415 19 : state = RELATIVE_WITH_PARENT_REF;
416 : }
417 : else
418 : {
419 : /* Append normal dir */
420 11861 : parsed = append_subdir_to_path(parsed, unparse);
421 11861 : state = RELATIVE_WITH_N_DEPTH;
422 11861 : pathdepth++;
423 : }
424 11880 : break;
425 17666 : case RELATIVE_WITH_N_DEPTH:
426 17666 : if (is_double_dot)
427 : {
428 : /* Remove last parsed dir */
429 27 : *parsed = '\0';
430 27 : parsed = trim_directory(path);
431 27 : 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 21 : if (parsed == spath)
440 12 : state = RELATIVE_PATH_INIT;
441 : else
442 9 : state = RELATIVE_WITH_PARENT_REF;
443 : }
444 : }
445 : else
446 : {
447 : /* Append normal dir */
448 17639 : *parsed++ = '/';
449 17639 : parsed = append_subdir_to_path(parsed, unparse);
450 17639 : pathdepth++;
451 : }
452 17666 : break;
453 25 : case RELATIVE_WITH_PARENT_REF:
454 25 : if (is_double_dot)
455 : {
456 : /* Append next irreducible double-dot (..) */
457 3 : *parsed++ = '/';
458 3 : parsed = append_subdir_to_path(parsed, unparse);
459 : }
460 : else
461 : {
462 : /* Append normal dir */
463 22 : *parsed++ = '/';
464 22 : 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 22 : state = RELATIVE_WITH_N_DEPTH;
472 22 : pathdepth = 1;
473 : }
474 25 : break;
475 : }
476 :
477 883701 : 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 112526 : if (parsed == spath)
487 30 : *parsed++ = '.';
488 :
489 : /* And finally, ensure the output path is nul-terminated. */
490 112526 : *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 6861 : 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 6861 : path = skip_drive(path); /* C: shouldn't affect our conclusion */
508 :
509 6861 : if (path[0] == '.' &&
510 5 : path[1] == '.' &&
511 1 : (path[2] == '\0' || path[2] == '/'))
512 1 : return true;
513 :
514 6860 : 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 6861 : path_is_relative_and_below_cwd(const char *path)
527 : {
528 6861 : if (is_absolute_path(path))
529 UBC 0 : return false;
530 : /* don't allow anything above the cwd */
531 CBC 6861 : else if (path_contains_parent_reference(path))
532 1 : 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 6860 : 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 48 : path_is_prefix_of_path(const char *path1, const char *path2)
560 : {
561 48 : int path1_len = strlen(path1);
562 :
563 48 : if (strncmp(path1, path2, path1_len) == 0 &&
564 1 : (IS_DIR_SEP(path2[path1_len]) || path2[path1_len] == '\0'))
565 1 : return true;
566 47 : 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 22075 : get_progname(const char *argv0)
575 : {
576 : const char *nodir_name;
577 : char *progname;
578 :
579 22075 : nodir_name = last_dir_separator(argv0);
580 22075 : if (nodir_name)
581 16032 : nodir_name++;
582 : else
583 6043 : 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 22075 : progname = strdup(nodir_name);
590 22075 : if (progname == NULL)
591 : {
592 UBC 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 CBC 22075 : 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 39923 : dir_strcmp(const char *s1, const char *s2)
613 : {
614 159692 : while (*s1 && *s2)
615 : {
616 119769 : if (
617 : #ifndef WIN32
618 119769 : *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 UBC 0 : && !(IS_DIR_SEP(*s1) && IS_DIR_SEP(*s2)))
624 0 : return (int) *s1 - (int) *s2;
625 CBC 119769 : s1++, s2++;
626 : }
627 39923 : if (*s1)
628 UBC 0 : return 1; /* s1 longer */
629 CBC 39923 : if (*s2)
630 UBC 0 : return -1; /* s2 longer */
631 CBC 39923 : 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 40730 : 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 40730 : prefix_len = 0;
673 1058980 : for (i = 0; target_path[i] && bin_path[i]; i++)
674 : {
675 1058980 : if (IS_DIR_SEP(target_path[i]) && IS_DIR_SEP(bin_path[i]))
676 162920 : prefix_len = i + 1;
677 896060 : else if (target_path[i] != bin_path[i])
678 40730 : break;
679 : }
680 40730 : if (prefix_len == 0)
681 UBC 0 : goto no_match; /* no common prefix? */
682 CBC 40730 : 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 40730 : strlcpy(ret_path, my_exec_path, MAXPGPATH);
689 40730 : trim_directory(ret_path); /* remove my executable name */
690 40730 : canonicalize_path(ret_path);
691 :
692 : /*
693 : * Tail match?
694 : */
695 40730 : tail_start = (int) strlen(ret_path) - tail_len;
696 40730 : if (tail_start > 0 &&
697 80653 : IS_DIR_SEP(ret_path[tail_start - 1]) &&
698 39923 : dir_strcmp(ret_path + tail_start, bin_path + prefix_len) == 0)
699 : {
700 39923 : ret_path[tail_start] = '\0';
701 39923 : trim_trailing_separator(ret_path);
702 39923 : join_path_components(ret_path, ret_path, target_path + prefix_len);
703 39923 : canonicalize_path(ret_path);
704 39923 : return;
705 : }
706 :
707 807 : no_match:
708 807 : strlcpy(ret_path, target_path, MAXPGPATH);
709 807 : 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 4096 : make_absolute_path(const char *path)
730 : {
731 : char *new;
732 :
733 : /* Returning null for null input is convenient for some callers */
734 4096 : if (path == NULL)
735 UBC 0 : return NULL;
736 :
737 CBC 4096 : if (!is_absolute_path(path))
738 : {
739 : char *buf;
740 : size_t buflen;
741 :
742 251 : buflen = MAXPGPATH;
743 : for (;;)
744 : {
745 UBC 0 : buf = malloc(buflen);
746 CBC 251 : if (!buf)
747 : {
748 : #ifndef FRONTEND
749 UBC 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 CBC 251 : if (getcwd(buf, buflen))
759 251 : break;
760 UBC 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: %s\n"),
776 0 : strerror(errno));
777 0 : return NULL;
778 : #endif
779 : }
780 : }
781 :
782 CBC 251 : new = malloc(strlen(buf) + strlen(path) + 2);
783 251 : if (!new)
784 : {
785 UBC 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 CBC 251 : sprintf(new, "%s/%s", buf, path);
796 251 : free(buf);
797 : }
798 : else
799 : {
800 3845 : new = strdup(path);
801 3845 : if (!new)
802 : {
803 : #ifndef FRONTEND
804 UBC 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 CBC 4096 : canonicalize_path(new);
816 :
817 4096 : return new;
818 : }
819 :
820 :
821 : /*
822 : * get_share_path
823 : */
824 : void
825 12865 : get_share_path(const char *my_exec_path, char *ret_path)
826 : {
827 12865 : make_relative_path(ret_path, PGSHAREDIR, PGBINDIR, my_exec_path);
828 12865 : }
829 :
830 : /*
831 : * get_etc_path
832 : */
833 : void
834 8411 : get_etc_path(const char *my_exec_path, char *ret_path)
835 : {
836 8411 : make_relative_path(ret_path, SYSCONFDIR, PGBINDIR, my_exec_path);
837 8411 : }
838 :
839 : /*
840 : * get_include_path
841 : */
842 : void
843 396 : get_include_path(const char *my_exec_path, char *ret_path)
844 : {
845 396 : make_relative_path(ret_path, INCLUDEDIR, PGBINDIR, my_exec_path);
846 396 : }
847 :
848 : /*
849 : * get_pkginclude_path
850 : */
851 : void
852 341 : get_pkginclude_path(const char *my_exec_path, char *ret_path)
853 : {
854 341 : make_relative_path(ret_path, PKGINCLUDEDIR, PGBINDIR, my_exec_path);
855 341 : }
856 :
857 : /*
858 : * get_includeserver_path
859 : */
860 : void
861 332 : get_includeserver_path(const char *my_exec_path, char *ret_path)
862 : {
863 332 : make_relative_path(ret_path, INCLUDEDIRSERVER, PGBINDIR, my_exec_path);
864 332 : }
865 :
866 : /*
867 : * get_lib_path
868 : */
869 : void
870 332 : get_lib_path(const char *my_exec_path, char *ret_path)
871 : {
872 332 : make_relative_path(ret_path, LIBDIR, PGBINDIR, my_exec_path);
873 332 : }
874 :
875 : /*
876 : * get_pkglib_path
877 : */
878 : void
879 2521 : get_pkglib_path(const char *my_exec_path, char *ret_path)
880 : {
881 2521 : make_relative_path(ret_path, PKGLIBDIR, PGBINDIR, my_exec_path);
882 2521 : }
883 :
884 : /*
885 : * get_locale_path
886 : */
887 : void
888 14536 : get_locale_path(const char *my_exec_path, char *ret_path)
889 : {
890 14536 : make_relative_path(ret_path, LOCALEDIR, PGBINDIR, my_exec_path);
891 14536 : }
892 :
893 : /*
894 : * get_doc_path
895 : */
896 : void
897 332 : get_doc_path(const char *my_exec_path, char *ret_path)
898 : {
899 332 : make_relative_path(ret_path, DOCDIR, PGBINDIR, my_exec_path);
900 332 : }
901 :
902 : /*
903 : * get_html_path
904 : */
905 : void
906 332 : get_html_path(const char *my_exec_path, char *ret_path)
907 : {
908 332 : make_relative_path(ret_path, HTMLDIR, PGBINDIR, my_exec_path);
909 332 : }
910 :
911 : /*
912 : * get_man_path
913 : */
914 : void
915 332 : get_man_path(const char *my_exec_path, char *ret_path)
916 : {
917 332 : make_relative_path(ret_path, MANDIR, PGBINDIR, my_exec_path);
918 332 : }
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 1 : 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 1 : home = getenv("HOME");
938 1 : if (home == NULL || home[0] == '\0')
939 UBC 0 : return pg_get_user_home_dir(geteuid(), ret_path, MAXPGPATH);
940 CBC 1 : strlcpy(ret_path, home, MAXPGPATH);
941 1 : return true;
942 : #else
943 : char *tmppath;
944 :
945 : /*
946 : * Note: We use getenv() here because the more modern SHGetFolderPath()
947 : * would force the backend to link with shell32.lib, which eats valuable
948 : * desktop heap. XXX This function is used only in psql, which already
949 : * brings in shell32 via libpq. Moving this function to its own file
950 : * would keep it out of the backend, freeing it from this concern.
951 : */
952 : tmppath = getenv("APPDATA");
953 : if (!tmppath)
954 : return false;
955 : snprintf(ret_path, MAXPGPATH, "%s/postgresql", tmppath);
956 : return true;
957 : #endif
958 : }
959 :
960 :
961 : /*
962 : * get_parent_directory
963 : *
964 : * Modify the given string in-place to name the parent directory of the
965 : * named file.
966 : *
967 : * If the input is just a file name with no directory part, the result is
968 : * an empty string, not ".". This is appropriate when the next step is
969 : * join_path_components(), but might need special handling otherwise.
970 : *
971 : * Caution: this will not produce desirable results if the string ends
972 : * with "..". For most callers this is not a problem since the string
973 : * is already known to name a regular file. If in doubt, apply
974 : * canonicalize_path() first.
975 : */
976 : void
977 5258 : get_parent_directory(char *path)
978 : {
979 5258 : trim_directory(path);
980 5258 : }
981 :
982 :
983 : /*
984 : * trim_directory
985 : *
986 : * Trim trailing directory from path, that is, remove any trailing slashes,
987 : * the last pathname component, and the slash just ahead of it --- but never
988 : * remove a leading slash.
989 : *
990 : * For the convenience of canonicalize_path, the path's new end location
991 : * is returned.
992 : */
993 : static char *
994 46065 : trim_directory(char *path)
995 : {
996 : char *p;
997 :
998 46065 : path = skip_drive(path);
999 :
1000 46065 : if (path[0] == '\0')
1001 UBC 0 : return path;
1002 :
1003 : /* back up over trailing slash(es) */
1004 CBC 46065 : for (p = path + strlen(path) - 1; IS_DIR_SEP(*p) && p > path; p--)
1005 : ;
1006 : /* back up over directory name */
1007 432903 : for (; !IS_DIR_SEP(*p) && p > path; p--)
1008 : ;
1009 : /* if multiple slashes before directory name, remove 'em all */
1010 46065 : for (; p > path && IS_DIR_SEP(*(p - 1)); p--)
1011 : ;
1012 : /* don't erase a leading slash */
1013 46065 : if (p == path && IS_DIR_SEP(*p))
1014 15 : p++;
1015 46065 : *p = '\0';
1016 46065 : return p;
1017 : }
1018 :
1019 :
1020 : /*
1021 : * trim_trailing_separator
1022 : *
1023 : * trim off trailing slashes, but not a leading slash
1024 : */
1025 : static void
1026 152477 : trim_trailing_separator(char *path)
1027 : {
1028 : char *p;
1029 :
1030 152477 : path = skip_drive(path);
1031 152477 : p = path + strlen(path);
1032 152477 : if (p > path)
1033 192391 : for (p--; p > path && IS_DIR_SEP(*p); p--)
1034 39942 : *p = '\0';
1035 152477 : }
1036 :
1037 : /*
1038 : * append_subdir_to_path
1039 : *
1040 : * Append the currently-considered subdirectory name to the output
1041 : * path in canonicalize_path. Return the new end location of the
1042 : * output path.
1043 : *
1044 : * Since canonicalize_path updates the path in-place, we must use
1045 : * memmove not memcpy, and we don't yet terminate the path with '\0'.
1046 : */
1047 : static char *
1048 883606 : append_subdir_to_path(char *path, char *subdir)
1049 : {
1050 883606 : size_t len = strlen(subdir);
1051 :
1052 : /* No need to copy data if path and subdir are the same. */
1053 883606 : if (path != subdir)
1054 205 : memmove(path, subdir, len);
1055 :
1056 883606 : return path + len;
1057 : }
|