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