/Users/deen/code/yugabyte-db/build/debugcov-clang-dynamic-arm64-ninja/postgres_build/src/interfaces/ecpg/ecpglib/path.c
Line | Count | Source (jump to first uncovered line) |
1 | | /*------------------------------------------------------------------------- |
2 | | * |
3 | | * path.c |
4 | | * portable path handling routines |
5 | | * |
6 | | * Portions Copyright (c) 1996-2018, 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 | 63.1k | #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 | 245k | #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 | 0 | { |
89 | | #ifdef WIN32 |
90 | | return skip_drive(path) != path; |
91 | | #else |
92 | 0 | return false; |
93 | 0 | #endif |
94 | 0 | } |
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 | 35.8k | { |
105 | 35.8k | const char *p; |
106 | | |
107 | 305k | for (p = skip_drive(filename); *p; p++) |
108 | 282k | if (IS_DIR_SEP(*p)) |
109 | 13.2k | return (char *) p; |
110 | 22.5k | return NULL; |
111 | 35.8k | } |
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 | 9.02k | { |
122 | 9.02k | const char *p; |
123 | | |
124 | | /* skip_drive is not needed */ |
125 | 72.1k | for (p = pathlist; *p; p++) |
126 | 63.1k | if (IS_PATH_VAR_SEP(*p)) |
127 | 0 | return (char *) p; |
128 | 9.02k | return NULL; |
129 | 9.02k | } |
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 | 17.3k | { |
140 | 17.3k | const char *p, |
141 | 17.3k | *ret = NULL; |
142 | | |
143 | 1.58M | for (p = skip_drive(filename); *p; p++) |
144 | 1.57M | if (IS_DIR_SEP(*p)) |
145 | 156k | ret = p; |
146 | 17.3k | return (char *) ret; |
147 | 17.3k | } |
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 | 0 | { |
168 | | #ifdef WIN32 |
169 | | char *p; |
170 | | |
171 | | for (p = filename; *p; p++) |
172 | | if (*p == '/') |
173 | | *p = '\\'; |
174 | | #endif |
175 | 0 | } |
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 | 0 | { |
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 | 0 | } |
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 | 25.4k | { |
221 | 25.4k | if (ret_path != head) |
222 | 25.4k | 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 | 25.4k | while (tail[0] == '.' && IS_DIR_SEP(tail[1])) |
231 | 0 | tail += 2; |
232 | | |
233 | 25.4k | if (*tail) |
234 | 25.4k | { |
235 | | /* only separate with slash if head wasn't empty */ |
236 | 25.4k | snprintf(ret_path + strlen(ret_path), MAXPGPATH - strlen(ret_path), |
237 | 25.4k | "%s%s", |
238 | 25.4k | (*(skip_drive(head)) != '\0') ? "/" : "", |
239 | 25.4k | tail); |
240 | 25.4k | } |
241 | 25.4k | } |
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 | 82.7k | { |
256 | 82.7k | char *p, |
257 | 82.7k | *to_p; |
258 | 82.7k | char *spath; |
259 | 82.7k | bool was_sep = false; |
260 | 82.7k | 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 | 82.7k | trim_trailing_separator(path); |
288 | | |
289 | | /* |
290 | | * Remove duplicate adjacent separators |
291 | | */ |
292 | 82.7k | p = path; |
293 | | #ifdef WIN32 |
294 | | /* Don't remove leading double-slash on Win32 */ |
295 | | if (*p) |
296 | | p++; |
297 | | #endif |
298 | 82.7k | to_p = p; |
299 | 6.35M | for (; *p; p++, to_p++) |
300 | 6.27M | { |
301 | | /* Handle many adjacent slashes, like "/a///b" */ |
302 | 6.27M | while (*p == '/' && was_sep) |
303 | 0 | p++; |
304 | 6.27M | if (to_p != p) |
305 | 0 | *to_p = *p; |
306 | 6.27M | was_sep = (*p == '/'); |
307 | 6.27M | } |
308 | 82.7k | *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 | 82.7k | spath = skip_drive(path); |
321 | 82.7k | pending_strips = 0; |
322 | 82.7k | for (;;) |
323 | 82.7k | { |
324 | 82.7k | int len = strlen(spath); |
325 | | |
326 | 82.7k | if (len >= 2 && strcmp(spath + len - 2, "/.") == 0) |
327 | 0 | trim_directory(path); |
328 | 82.7k | else if (strcmp(spath, ".") == 0) |
329 | 0 | { |
330 | | /* Want to leave "." alone, but "./.." has to become ".." */ |
331 | 0 | if (pending_strips > 0) |
332 | 0 | *spath = '\0'; |
333 | 0 | break; |
334 | 0 | } |
335 | 82.7k | else if ((len >= 3 && strcmp(spath + len - 3, "/..") == 0) || |
336 | 82.7k | strcmp(spath, "..") == 0) |
337 | 0 | { |
338 | 0 | trim_directory(path); |
339 | 0 | pending_strips++; |
340 | 0 | } |
341 | 82.7k | else if (pending_strips > 0 && *spath != '\0') |
342 | 0 | { |
343 | | /* trim a regular directory name canceled by ".." */ |
344 | 0 | trim_directory(path); |
345 | 0 | pending_strips--; |
346 | | /* foo/.. should become ".", not empty */ |
347 | 0 | if (*spath == '\0') |
348 | 0 | strcpy(spath, "."); |
349 | 0 | } |
350 | 82.7k | else |
351 | 82.7k | break; |
352 | 82.7k | } |
353 | | |
354 | 82.7k | if (pending_strips > 0) |
355 | 0 | { |
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 | 0 | while (--pending_strips > 0) |
362 | 0 | strcat(path, "../"); |
363 | 0 | strcat(path, ".."); |
364 | 0 | } |
365 | 82.7k | } |
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 | 12 | { |
378 | 12 | int path_len; |
379 | | |
380 | 12 | path = skip_drive(path); /* C: shouldn't affect our conclusion */ |
381 | | |
382 | 12 | 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 | 12 | if (strcmp(path, "..") == 0 || |
389 | 12 | strncmp(path, "../", 3) == 0 || |
390 | 11 | strstr(path, "/../") != NULL || |
391 | 10 | (path_len >= 3 && strcmp(path + path_len - 3, "/..") == 0)) |
392 | 2 | return true; |
393 | | |
394 | 10 | return false; |
395 | 10 | } |
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 | 1 | { |
407 | 1 | if (is_absolute_path(path)) |
408 | 0 | return false; |
409 | | /* don't allow anything above the cwd */ |
410 | 1 | else if (path_contains_parent_reference(path)) |
411 | 1 | 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 | 1 | else |
428 | 0 | return true; |
429 | 1 | } |
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 | 10 | { |
440 | 10 | int path1_len = strlen(path1); |
441 | | |
442 | 10 | if (strncmp(path1, path2, path1_len) == 0 && |
443 | 1 | (IS_DIR_SEP(path2[path1_len]) || path2[path1_len] == '\0')) |
444 | 1 | return true; |
445 | 9 | return false; |
446 | 9 | } |
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 | 5.48k | { |
455 | 5.48k | const char *nodir_name; |
456 | 5.48k | char *progname; |
457 | | |
458 | 5.48k | nodir_name = last_dir_separator(argv0); |
459 | 5.48k | if (nodir_name) |
460 | 5.48k | nodir_name++; |
461 | 0 | else |
462 | 0 | 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 | 5.48k | progname = strdup(nodir_name); |
469 | 5.48k | if (progname == NULL) |
470 | 0 | { |
471 | 0 | fprintf(stderr, "%s: out of memory\n", nodir_name); |
472 | 0 | abort(); /* This could exit the postmaster */ |
473 | 0 | } |
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 | 5.48k | return progname; |
483 | 5.48k | } |
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 | 11.8k | { |
493 | 47.4k | while (*s1 && *s2) |
494 | 35.5k | { |
495 | 35.5k | if ( |
496 | 35.5k | #ifndef WIN32 |
497 | 35.5k | *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 | 0 | && !(IS_DIR_SEP(*s1) && IS_DIR_SEP(*s2))) |
503 | 0 | return (int) *s1 - (int) *s2; |
504 | 35.5k | s1++, s2++; |
505 | 35.5k | } |
506 | 11.8k | if (*s1) |
507 | 0 | return 1; /* s1 longer */ |
508 | 11.8k | if (*s2) |
509 | 0 | return -1; /* s2 longer */ |
510 | 11.8k | return 0; |
511 | 11.8k | } |
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 | 11.8k | { |
542 | 11.8k | int prefix_len; |
543 | 11.8k | int tail_start; |
544 | 11.8k | int tail_len; |
545 | 11.8k | 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 | 11.8k | prefix_len = 0; |
552 | 949k | for (i = 0; target_path[i] && bin_path[i]; i++) |
553 | 949k | { |
554 | 949k | if (IS_DIR_SEP(target_path[i]) && IS_DIR_SEP(bin_path[i])) |
555 | 94.9k | prefix_len = i + 1; |
556 | 854k | else if (target_path[i] != bin_path[i]) |
557 | 11.8k | break; |
558 | 949k | } |
559 | 11.8k | if (prefix_len == 0) |
560 | 0 | goto no_match; /* no common prefix? */ |
561 | 11.8k | 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 | 11.8k | strlcpy(ret_path, my_exec_path, MAXPGPATH); |
568 | 11.8k | trim_directory(ret_path); /* remove my executable name */ |
569 | 11.8k | canonicalize_path(ret_path); |
570 | | |
571 | | /* |
572 | | * Tail match? |
573 | | */ |
574 | 11.8k | tail_start = (int) strlen(ret_path) - tail_len; |
575 | 11.8k | if (tail_start > 0 && |
576 | 11.8k | IS_DIR_SEP(ret_path[tail_start - 1]) && |
577 | 11.8k | dir_strcmp(ret_path + tail_start, bin_path + prefix_len) == 0) |
578 | 11.8k | { |
579 | 11.8k | ret_path[tail_start] = '\0'; |
580 | 11.8k | trim_trailing_separator(ret_path); |
581 | 11.8k | join_path_components(ret_path, ret_path, target_path + prefix_len); |
582 | 11.8k | canonicalize_path(ret_path); |
583 | 11.8k | return; |
584 | 11.8k | } |
585 | | |
586 | 14 | no_match: |
587 | 14 | strlcpy(ret_path, target_path, MAXPGPATH); |
588 | 14 | canonicalize_path(ret_path); |
589 | 14 | } |
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 | 42 | { |
610 | 42 | char *new; |
611 | | |
612 | | /* Returning null for null input is convenient for some callers */ |
613 | 42 | if (path == NULL) |
614 | 0 | return NULL; |
615 | | |
616 | 42 | if (!is_absolute_path(path)) |
617 | 0 | { |
618 | 0 | char *buf; |
619 | 0 | size_t buflen; |
620 | |
|
621 | 0 | buflen = MAXPGPATH; |
622 | 0 | for (;;) |
623 | 0 | { |
624 | 0 | buf = malloc(buflen); |
625 | 0 | if (!buf) |
626 | 0 | { |
627 | | #ifndef FRONTEND |
628 | | ereport(ERROR, |
629 | | (errcode(ERRCODE_OUT_OF_MEMORY), |
630 | | errmsg("out of memory"))); |
631 | | #else |
632 | 0 | fprintf(stderr, _("out of memory\n")); |
633 | 0 | return NULL; |
634 | 0 | #endif |
635 | 0 | } |
636 | | |
637 | 0 | if (getcwd(buf, buflen)) |
638 | 0 | break; |
639 | 0 | else if (errno == ERANGE) |
640 | 0 | { |
641 | 0 | free(buf); |
642 | 0 | buflen *= 2; |
643 | 0 | continue; |
644 | 0 | } |
645 | 0 | else |
646 | 0 | { |
647 | 0 | int save_errno = errno; |
648 | |
|
649 | 0 | free(buf); |
650 | 0 | errno = save_errno; |
651 | | #ifndef FRONTEND |
652 | | elog(ERROR, "could not get current working directory: %m"); |
653 | | #else |
654 | 0 | fprintf(stderr, _("could not get current working directory: %s\n"), |
655 | 0 | strerror(errno)); |
656 | 0 | return NULL; |
657 | 0 | #endif |
658 | 0 | } |
659 | 0 | } |
660 | |
|
661 | 0 | new = malloc(strlen(buf) + strlen(path) + 2); |
662 | 0 | if (!new) |
663 | 0 | { |
664 | 0 | free(buf); |
665 | | #ifndef FRONTEND |
666 | | ereport(ERROR, |
667 | | (errcode(ERRCODE_OUT_OF_MEMORY), |
668 | | errmsg("out of memory"))); |
669 | | #else |
670 | 0 | fprintf(stderr, _("out of memory\n")); |
671 | 0 | return NULL; |
672 | 0 | #endif |
673 | 0 | } |
674 | 0 | sprintf(new, "%s/%s", buf, path); |
675 | 0 | free(buf); |
676 | 0 | } |
677 | 42 | else |
678 | 42 | { |
679 | 42 | new = strdup(path); |
680 | 42 | if (!new) |
681 | 0 | { |
682 | | #ifndef FRONTEND |
683 | | ereport(ERROR, |
684 | | (errcode(ERRCODE_OUT_OF_MEMORY), |
685 | | errmsg("out of memory"))); |
686 | | #else |
687 | 0 | fprintf(stderr, _("out of memory\n")); |
688 | 0 | return NULL; |
689 | 0 | #endif |
690 | 0 | } |
691 | 42 | } |
692 | | |
693 | | /* Make sure punctuation is canonical, too */ |
694 | 42 | canonicalize_path(new); |
695 | | |
696 | 42 | return new; |
697 | 42 | } |
698 | | |
699 | | |
700 | | /* |
701 | | * get_share_path |
702 | | */ |
703 | | void |
704 | | get_share_path(const char *my_exec_path, char *ret_path) |
705 | 6.42k | { |
706 | 6.42k | make_relative_path(ret_path, PGSHAREDIR, PGBINDIR, my_exec_path); |
707 | 6.42k | } |
708 | | |
709 | | /* |
710 | | * get_etc_path |
711 | | */ |
712 | | void |
713 | | get_etc_path(const char *my_exec_path, char *ret_path) |
714 | 1.82k | { |
715 | 1.82k | make_relative_path(ret_path, SYSCONFDIR, PGBINDIR, my_exec_path); |
716 | 1.82k | } |
717 | | |
718 | | /* |
719 | | * get_include_path |
720 | | */ |
721 | | void |
722 | | get_include_path(const char *my_exec_path, char *ret_path) |
723 | 0 | { |
724 | 0 | make_relative_path(ret_path, INCLUDEDIR, PGBINDIR, my_exec_path); |
725 | 0 | } |
726 | | |
727 | | /* |
728 | | * get_pkginclude_path |
729 | | */ |
730 | | void |
731 | | get_pkginclude_path(const char *my_exec_path, char *ret_path) |
732 | 0 | { |
733 | 0 | make_relative_path(ret_path, PKGINCLUDEDIR, PGBINDIR, my_exec_path); |
734 | 0 | } |
735 | | |
736 | | /* |
737 | | * get_includeserver_path |
738 | | */ |
739 | | void |
740 | | get_includeserver_path(const char *my_exec_path, char *ret_path) |
741 | 0 | { |
742 | 0 | make_relative_path(ret_path, INCLUDEDIRSERVER, PGBINDIR, my_exec_path); |
743 | 0 | } |
744 | | |
745 | | /* |
746 | | * get_lib_path |
747 | | */ |
748 | | void |
749 | | get_lib_path(const char *my_exec_path, char *ret_path) |
750 | 0 | { |
751 | 0 | make_relative_path(ret_path, LIBDIR, PGBINDIR, my_exec_path); |
752 | 0 | } |
753 | | |
754 | | /* |
755 | | * get_pkglib_path |
756 | | */ |
757 | | void |
758 | | get_pkglib_path(const char *my_exec_path, char *ret_path) |
759 | 3.61k | { |
760 | 3.61k | make_relative_path(ret_path, PKGLIBDIR, PGBINDIR, my_exec_path); |
761 | 3.61k | } |
762 | | |
763 | | /* |
764 | | * get_locale_path |
765 | | */ |
766 | | void |
767 | | get_locale_path(const char *my_exec_path, char *ret_path) |
768 | 0 | { |
769 | 0 | make_relative_path(ret_path, LOCALEDIR, PGBINDIR, my_exec_path); |
770 | 0 | } |
771 | | |
772 | | /* |
773 | | * get_doc_path |
774 | | */ |
775 | | void |
776 | | get_doc_path(const char *my_exec_path, char *ret_path) |
777 | 0 | { |
778 | 0 | make_relative_path(ret_path, DOCDIR, PGBINDIR, my_exec_path); |
779 | 0 | } |
780 | | |
781 | | /* |
782 | | * get_html_path |
783 | | */ |
784 | | void |
785 | | get_html_path(const char *my_exec_path, char *ret_path) |
786 | 0 | { |
787 | 0 | make_relative_path(ret_path, HTMLDIR, PGBINDIR, my_exec_path); |
788 | 0 | } |
789 | | |
790 | | /* |
791 | | * get_man_path |
792 | | */ |
793 | | void |
794 | | get_man_path(const char *my_exec_path, char *ret_path) |
795 | 0 | { |
796 | 0 | make_relative_path(ret_path, MANDIR, PGBINDIR, my_exec_path); |
797 | 0 | } |
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 | 1 | { |
809 | 1 | #ifndef WIN32 |
810 | 1 | char pwdbuf[BUFSIZ]; |
811 | 1 | struct passwd pwdstr; |
812 | 1 | struct passwd *pwd = NULL; |
813 | | |
814 | 1 | (void) pqGetpwuid(geteuid(), &pwdstr, pwdbuf, sizeof(pwdbuf), &pwd); |
815 | 1 | if (pwd == NULL) |
816 | 0 | return false; |
817 | 1 | strlcpy(ret_path, pwd->pw_dir, MAXPGPATH); |
818 | 1 | 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 | 1 | } |
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 | 2.76k | { |
856 | 2.76k | trim_directory(path); |
857 | 2.76k | } |
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 | 14.6k | { |
870 | 14.6k | char *p; |
871 | | |
872 | 14.6k | path = skip_drive(path); |
873 | | |
874 | 14.6k | if (path[0] == '\0') |
875 | 0 | return; |
876 | | |
877 | | /* back up over trailing slash(es) */ |
878 | 14.6k | for (p = path + strlen(path) - 1; IS_DIR_SEP(*p) && p > path; p--) |
879 | 0 | ; |
880 | | /* back up over directory name */ |
881 | 170k | for (; !IS_DIR_SEP(*p) && p > path; p--) |
882 | 155k | ; |
883 | | /* if multiple slashes before directory name, remove 'em all */ |
884 | 14.6k | for (; p > path && IS_DIR_SEP(*(p - 1)); p--) |
885 | 0 | ; |
886 | | /* don't erase a leading slash */ |
887 | 14.6k | if (p == path && IS_DIR_SEP(*p)) |
888 | 0 | p++; |
889 | 14.6k | *p = '\0'; |
890 | 14.6k | } |
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 | 94.5k | { |
901 | 94.5k | char *p; |
902 | | |
903 | 94.5k | path = skip_drive(path); |
904 | 94.5k | p = path + strlen(path); |
905 | 94.5k | if (p > path) |
906 | 106k | for (p--; p > path && IS_DIR_SEP(*p); p--) |
907 | 11.8k | *p = '\0'; |
908 | 94.5k | } |