PostgreSQL Source Code  git master
path.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * path.c
4  * portable path handling routines
5  *
6  * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  * src/port/path.c
12  *
13  *-------------------------------------------------------------------------
14  */
15 
16 #ifndef FRONTEND
17 #include "postgres.h"
18 #else
19 #include "postgres_fe.h"
20 #endif
21 
22 #include <ctype.h>
23 #include <sys/stat.h>
24 #ifdef WIN32
25 #ifdef _WIN32_IE
26 #undef _WIN32_IE
27 #endif
28 #define _WIN32_IE 0x0500
29 #ifdef near
30 #undef near
31 #endif
32 #define near
33 #include <shlobj.h>
34 #else
35 #include <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 has_drive_prefix(const char *path)
89 {
90 #ifdef WIN32
91  return skip_drive(path) != path;
92 #else
93  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 *
105 {
106  const char *p;
107 
108  for (p = skip_drive(filename); *p; p++)
109  if (IS_DIR_SEP(*p))
110  return unconstify(char *, p);
111  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 first_path_var_separator(const char *pathlist)
122 {
123  const char *p;
124 
125  /* skip_drive is not needed */
126  for (p = pathlist; *p; p++)
127  if (IS_PATH_VAR_SEP(*p))
128  return unconstify(char *, p);
129  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 *
140 {
141  const char *p,
142  *ret = NULL;
143 
144  for (p = skip_drive(filename); *p; p++)
145  if (IS_DIR_SEP(*p))
146  ret = p;
147  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
168 {
169 #ifdef WIN32
170  char *p;
171 
172  for (p = filename; *p; p++)
173  if (*p == '/')
174  *p = '\\';
175 #endif
176 }
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 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 }
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 join_path_components(char *ret_path,
220  const char *head, const char *tail)
221 {
222  if (ret_path != head)
223  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  if (*tail)
231  {
232  /* only separate with slash if head wasn't empty */
233  snprintf(ret_path + strlen(ret_path), MAXPGPATH - strlen(ret_path),
234  "%s%s",
235  (*(skip_drive(head)) != '\0') ? "/" : "",
236  tail);
237  }
238 }
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 */
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 canonicalize_path(char *path)
265 {
266  char *p,
267  *to_p;
268  char *spath;
269  char *parsed;
270  char *unparse;
271  bool was_sep = false;
273  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  */
301 
302  /*
303  * Remove duplicate adjacent separators
304  */
305  p = path;
306 #ifdef WIN32
307  /* Don't remove leading double-slash on Win32 */
308  if (*p)
309  p++;
310 #endif
311  to_p = p;
312  for (; *p; p++, to_p++)
313  {
314  /* Handle many adjacent slashes, like "/a///b" */
315  while (*p == '/' && was_sep)
316  p++;
317  if (to_p != p)
318  *to_p = *p;
319  was_sep = (*p == '/');
320  }
321  *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  spath = skip_drive(path);
336  if (*spath == '\0')
337  return; /* empty path is returned as-is */
338 
339  if (*spath == '/')
340  {
342  /* Skip the leading slash for absolute path */
343  parsed = unparse = (spath + 1);
344  }
345  else
346  {
348  parsed = unparse = spath;
349  }
350 
351  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  unparse_next = unparse;
358  while (*unparse_next && *unparse_next != '/')
359  unparse_next++;
360  if (*unparse_next != '\0')
361  *unparse_next++ = '\0';
362 
363  /* Identify type of this dir name */
364  if (strcmp(unparse, ".") == 0)
365  {
366  /* We can ignore "." components in all cases */
367  unparse = unparse_next;
368  continue;
369  }
370 
371  if (strcmp(unparse, "..") == 0)
372  is_double_dot = true;
373  else
374  {
375  /* adjacent separators were eliminated above */
376  Assert(*unparse != '\0');
377  is_double_dot = false;
378  }
379 
380  switch (state)
381  {
382  case ABSOLUTE_PATH_INIT:
383  /* We can ignore ".." immediately after / */
384  if (!is_double_dot)
385  {
386  /* Append first dir name (we already have leading slash) */
387  parsed = append_subdir_to_path(parsed, unparse);
389  pathdepth++;
390  }
391  break;
393  if (is_double_dot)
394  {
395  /* Remove last parsed dir */
396  /* (trim_directory won't remove the leading slash) */
397  *parsed = '\0';
398  parsed = trim_directory(path);
399  if (--pathdepth == 0)
401  }
402  else
403  {
404  /* Append normal dir */
405  *parsed++ = '/';
406  parsed = append_subdir_to_path(parsed, unparse);
407  pathdepth++;
408  }
409  break;
410  case RELATIVE_PATH_INIT:
411  if (is_double_dot)
412  {
413  /* Append irreducible double-dot (..) */
414  parsed = append_subdir_to_path(parsed, unparse);
416  }
417  else
418  {
419  /* Append normal dir */
420  parsed = append_subdir_to_path(parsed, unparse);
422  pathdepth++;
423  }
424  break;
426  if (is_double_dot)
427  {
428  /* Remove last parsed dir */
429  *parsed = '\0';
430  parsed = trim_directory(path);
431  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  if (parsed == spath)
441  else
443  }
444  }
445  else
446  {
447  /* Append normal dir */
448  *parsed++ = '/';
449  parsed = append_subdir_to_path(parsed, unparse);
450  pathdepth++;
451  }
452  break;
454  if (is_double_dot)
455  {
456  /* Append next irreducible double-dot (..) */
457  *parsed++ = '/';
458  parsed = append_subdir_to_path(parsed, unparse);
459  }
460  else
461  {
462  /* Append normal dir */
463  *parsed++ = '/';
464  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  */
472  pathdepth = 1;
473  }
474  break;
475  }
476 
477  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  if (parsed == spath)
487  *parsed++ = '.';
488 
489  /* And finally, ensure the output path is nul-terminated. */
490  *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
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  path = skip_drive(path); /* C: shouldn't affect our conclusion */
508 
509  if (path[0] == '.' &&
510  path[1] == '.' &&
511  (path[2] == '\0' || path[2] == '/'))
512  return true;
513 
514  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
527 {
528  if (is_absolute_path(path))
529  return false;
530  /* don't allow anything above the cwd */
531  else if (path_contains_parent_reference(path))
532  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  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 path_is_prefix_of_path(const char *path1, const char *path2)
560 {
561  int path1_len = strlen(path1);
562 
563  if (strncmp(path1, path2, path1_len) == 0 &&
564  (IS_DIR_SEP(path2[path1_len]) || path2[path1_len] == '\0'))
565  return true;
566  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 get_progname(const char *argv0)
575 {
576  const char *nodir_name;
577  char *progname;
578 
579  nodir_name = last_dir_separator(argv0);
580  if (nodir_name)
581  nodir_name++;
582  else
583  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  progname = strdup(nodir_name);
590  if (progname == NULL)
591  {
592  fprintf(stderr, "%s: out of memory\n", nodir_name);
593  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  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 dir_strcmp(const char *s1, const char *s2)
613 {
614  while (*s1 && *s2)
615  {
616  if (
617 #ifndef WIN32
618  *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  && !(IS_DIR_SEP(*s1) && IS_DIR_SEP(*s2)))
624  return (int) *s1 - (int) *s2;
625  s1++, s2++;
626  }
627  if (*s1)
628  return 1; /* s1 longer */
629  if (*s2)
630  return -1; /* s2 longer */
631  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 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  prefix_len = 0;
673  for (i = 0; target_path[i] && bin_path[i]; i++)
674  {
675  if (IS_DIR_SEP(target_path[i]) && IS_DIR_SEP(bin_path[i]))
676  prefix_len = i + 1;
677  else if (target_path[i] != bin_path[i])
678  break;
679  }
680  if (prefix_len == 0)
681  goto no_match; /* no common prefix? */
682  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  strlcpy(ret_path, my_exec_path, MAXPGPATH);
689  trim_directory(ret_path); /* remove my executable name */
690  canonicalize_path(ret_path);
691 
692  /*
693  * Tail match?
694  */
695  tail_start = (int) strlen(ret_path) - tail_len;
696  if (tail_start > 0 &&
697  IS_DIR_SEP(ret_path[tail_start - 1]) &&
698  dir_strcmp(ret_path + tail_start, bin_path + prefix_len) == 0)
699  {
700  ret_path[tail_start] = '\0';
701  trim_trailing_separator(ret_path);
702  join_path_components(ret_path, ret_path, target_path + prefix_len);
703  canonicalize_path(ret_path);
704  return;
705  }
706 
707 no_match:
708  strlcpy(ret_path, target_path, MAXPGPATH);
709  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 make_absolute_path(const char *path)
730 {
731  char *new;
732 
733  /* Returning null for null input is convenient for some callers */
734  if (path == NULL)
735  return NULL;
736 
737  if (!is_absolute_path(path))
738  {
739  char *buf;
740  size_t buflen;
741 
742  buflen = MAXPGPATH;
743  for (;;)
744  {
745  buf = malloc(buflen);
746  if (!buf)
747  {
748 #ifndef FRONTEND
749  ereport(ERROR,
750  (errcode(ERRCODE_OUT_OF_MEMORY),
751  errmsg("out of memory")));
752 #else
753  fprintf(stderr, _("out of memory\n"));
754  return NULL;
755 #endif
756  }
757 
758  if (getcwd(buf, buflen))
759  break;
760  else if (errno == ERANGE)
761  {
762  free(buf);
763  buflen *= 2;
764  continue;
765  }
766  else
767  {
768  int save_errno = errno;
769 
770  free(buf);
771  errno = save_errno;
772 #ifndef FRONTEND
773  elog(ERROR, "could not get current working directory: %m");
774 #else
775  fprintf(stderr, _("could not get current working directory: %m\n"));
776  return NULL;
777 #endif
778  }
779  }
780 
781  new = malloc(strlen(buf) + strlen(path) + 2);
782  if (!new)
783  {
784  free(buf);
785 #ifndef FRONTEND
786  ereport(ERROR,
787  (errcode(ERRCODE_OUT_OF_MEMORY),
788  errmsg("out of memory")));
789 #else
790  fprintf(stderr, _("out of memory\n"));
791  return NULL;
792 #endif
793  }
794  sprintf(new, "%s/%s", buf, path);
795  free(buf);
796  }
797  else
798  {
799  new = strdup(path);
800  if (!new)
801  {
802 #ifndef FRONTEND
803  ereport(ERROR,
804  (errcode(ERRCODE_OUT_OF_MEMORY),
805  errmsg("out of memory")));
806 #else
807  fprintf(stderr, _("out of memory\n"));
808  return NULL;
809 #endif
810  }
811  }
812 
813  /* Make sure punctuation is canonical, too */
814  canonicalize_path(new);
815 
816  return new;
817 }
818 
819 
820 /*
821  * get_share_path
822  */
823 void
824 get_share_path(const char *my_exec_path, char *ret_path)
825 {
826  make_relative_path(ret_path, PGSHAREDIR, PGBINDIR, my_exec_path);
827 }
828 
829 /*
830  * get_etc_path
831  */
832 void
833 get_etc_path(const char *my_exec_path, char *ret_path)
834 {
835  make_relative_path(ret_path, SYSCONFDIR, PGBINDIR, my_exec_path);
836 }
837 
838 /*
839  * get_include_path
840  */
841 void
842 get_include_path(const char *my_exec_path, char *ret_path)
843 {
844  make_relative_path(ret_path, INCLUDEDIR, PGBINDIR, my_exec_path);
845 }
846 
847 /*
848  * get_pkginclude_path
849  */
850 void
851 get_pkginclude_path(const char *my_exec_path, char *ret_path)
852 {
853  make_relative_path(ret_path, PKGINCLUDEDIR, PGBINDIR, my_exec_path);
854 }
855 
856 /*
857  * get_includeserver_path
858  */
859 void
860 get_includeserver_path(const char *my_exec_path, char *ret_path)
861 {
862  make_relative_path(ret_path, INCLUDEDIRSERVER, PGBINDIR, my_exec_path);
863 }
864 
865 /*
866  * get_lib_path
867  */
868 void
869 get_lib_path(const char *my_exec_path, char *ret_path)
870 {
871  make_relative_path(ret_path, LIBDIR, PGBINDIR, my_exec_path);
872 }
873 
874 /*
875  * get_pkglib_path
876  */
877 void
878 get_pkglib_path(const char *my_exec_path, char *ret_path)
879 {
880  make_relative_path(ret_path, PKGLIBDIR, PGBINDIR, my_exec_path);
881 }
882 
883 /*
884  * get_locale_path
885  */
886 void
887 get_locale_path(const char *my_exec_path, char *ret_path)
888 {
889  make_relative_path(ret_path, LOCALEDIR, PGBINDIR, my_exec_path);
890 }
891 
892 /*
893  * get_doc_path
894  */
895 void
896 get_doc_path(const char *my_exec_path, char *ret_path)
897 {
898  make_relative_path(ret_path, DOCDIR, PGBINDIR, my_exec_path);
899 }
900 
901 /*
902  * get_html_path
903  */
904 void
905 get_html_path(const char *my_exec_path, char *ret_path)
906 {
907  make_relative_path(ret_path, HTMLDIR, PGBINDIR, my_exec_path);
908 }
909 
910 /*
911  * get_man_path
912  */
913 void
914 get_man_path(const char *my_exec_path, char *ret_path)
915 {
916  make_relative_path(ret_path, MANDIR, PGBINDIR, my_exec_path);
917 }
918 
919 
920 /*
921  * get_home_path
922  *
923  * On Unix, this actually returns the user's home directory. On Windows
924  * it returns the PostgreSQL-specific application data folder.
925  */
926 bool
927 get_home_path(char *ret_path)
928 {
929 #ifndef WIN32
930  /*
931  * We first consult $HOME. If that's unset, try to get the info from
932  * <pwd.h>.
933  */
934  const char *home;
935 
936  home = getenv("HOME");
937  if (home == NULL || home[0] == '\0')
938  return pg_get_user_home_dir(geteuid(), ret_path, MAXPGPATH);
939  strlcpy(ret_path, home, MAXPGPATH);
940  return true;
941 #else
942  char *tmppath;
943 
944  /*
945  * Note: We use getenv() here because the more modern SHGetFolderPath()
946  * would force the backend to link with shell32.lib, which eats valuable
947  * desktop heap. XXX This function is used only in psql, which already
948  * brings in shell32 via libpq. Moving this function to its own file
949  * would keep it out of the backend, freeing it from this concern.
950  */
951  tmppath = getenv("APPDATA");
952  if (!tmppath)
953  return false;
954  snprintf(ret_path, MAXPGPATH, "%s/postgresql", tmppath);
955  return true;
956 #endif
957 }
958 
959 
960 /*
961  * get_parent_directory
962  *
963  * Modify the given string in-place to name the parent directory of the
964  * named file.
965  *
966  * If the input is just a file name with no directory part, the result is
967  * an empty string, not ".". This is appropriate when the next step is
968  * join_path_components(), but might need special handling otherwise.
969  *
970  * Caution: this will not produce desirable results if the string ends
971  * with "..". For most callers this is not a problem since the string
972  * is already known to name a regular file. If in doubt, apply
973  * canonicalize_path() first.
974  */
975 void
977 {
978  trim_directory(path);
979 }
980 
981 
982 /*
983  * trim_directory
984  *
985  * Trim trailing directory from path, that is, remove any trailing slashes,
986  * the last pathname component, and the slash just ahead of it --- but never
987  * remove a leading slash.
988  *
989  * For the convenience of canonicalize_path, the path's new end location
990  * is returned.
991  */
992 static char *
993 trim_directory(char *path)
994 {
995  char *p;
996 
997  path = skip_drive(path);
998 
999  if (path[0] == '\0')
1000  return path;
1001 
1002  /* back up over trailing slash(es) */
1003  for (p = path + strlen(path) - 1; IS_DIR_SEP(*p) && p > path; p--)
1004  ;
1005  /* back up over directory name */
1006  for (; !IS_DIR_SEP(*p) && p > path; p--)
1007  ;
1008  /* if multiple slashes before directory name, remove 'em all */
1009  for (; p > path && IS_DIR_SEP(*(p - 1)); p--)
1010  ;
1011  /* don't erase a leading slash */
1012  if (p == path && IS_DIR_SEP(*p))
1013  p++;
1014  *p = '\0';
1015  return p;
1016 }
1017 
1018 
1019 /*
1020  * trim_trailing_separator
1021  *
1022  * trim off trailing slashes, but not a leading slash
1023  */
1024 static void
1026 {
1027  char *p;
1028 
1029  path = skip_drive(path);
1030  p = path + strlen(path);
1031  if (p > path)
1032  for (p--; p > path && IS_DIR_SEP(*p); p--)
1033  *p = '\0';
1034 }
1035 
1036 /*
1037  * append_subdir_to_path
1038  *
1039  * Append the currently-considered subdirectory name to the output
1040  * path in canonicalize_path. Return the new end location of the
1041  * output path.
1042  *
1043  * Since canonicalize_path updates the path in-place, we must use
1044  * memmove not memcpy, and we don't yet terminate the path with '\0'.
1045  */
1046 static char *
1047 append_subdir_to_path(char *path, char *subdir)
1048 {
1049  size_t len = strlen(subdir);
1050 
1051  /* No need to copy data if path and subdir are the same. */
1052  if (path != subdir)
1053  memmove(path, subdir, len);
1054 
1055  return path + len;
1056 }
#define unconstify(underlying_type, expr)
Definition: c.h:1245
#define Assert(condition)
Definition: c.h:858
int errcode(int sqlerrcode)
Definition: elog.c:859
int errmsg(const char *fmt,...)
Definition: elog.c:1072
#define _(x)
Definition: elog.c:90
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:224
#define ereport(elevel,...)
Definition: elog.h:149
char my_exec_path[MAXPGPATH]
Definition: globals.c:78
#define free(a)
Definition: header.h:65
#define malloc(a)
Definition: header.h:50
static char bin_path[MAXPGPATH]
Definition: initdb.c:257
int i
Definition: isn.c:73
const char * progname
Definition: main.c:44
void cleanup_path(char *path)
Definition: path.c:186
void get_share_path(const char *my_exec_path, char *ret_path)
Definition: path.c:824
void get_pkglib_path(const char *my_exec_path, char *ret_path)
Definition: path.c:878
void get_locale_path(const char *my_exec_path, char *ret_path)
Definition: path.c:887
void join_path_components(char *ret_path, const char *head, const char *tail)
Definition: path.c:219
#define IS_PATH_VAR_SEP(ch)
Definition: path.c:42
char * last_dir_separator(const char *filename)
Definition: path.c:139
void get_man_path(const char *my_exec_path, char *ret_path)
Definition: path.c:914
bool get_home_path(char *ret_path)
Definition: path.c:927
char * make_absolute_path(const char *path)
Definition: path.c:729
void get_include_path(const char *my_exec_path, char *ret_path)
Definition: path.c:842
static char * append_subdir_to_path(char *path, char *subdir)
Definition: path.c:1047
bool path_is_prefix_of_path(const char *path1, const char *path2)
Definition: path.c:559
char * first_dir_separator(const char *filename)
Definition: path.c:104
bool path_is_relative_and_below_cwd(const char *path)
Definition: path.c:526
void get_lib_path(const char *my_exec_path, char *ret_path)
Definition: path.c:869
void canonicalize_path(char *path)
Definition: path.c:264
void get_parent_directory(char *path)
Definition: path.c:976
const char * get_progname(const char *argv0)
Definition: path.c:574
static void trim_trailing_separator(char *path)
Definition: path.c:1025
static char * trim_directory(char *path)
Definition: path.c:993
static void make_relative_path(char *ret_path, const char *target_path, const char *bin_path, const char *my_exec_path)
Definition: path.c:660
#define skip_drive(path)
Definition: path.c:79
void get_etc_path(const char *my_exec_path, char *ret_path)
Definition: path.c:833
static int dir_strcmp(const char *s1, const char *s2)
Definition: path.c:612
bool path_contains_parent_reference(const char *path)
Definition: path.c:499
char * first_path_var_separator(const char *pathlist)
Definition: path.c:121
void make_native_path(char *filename)
Definition: path.c:167
void get_doc_path(const char *my_exec_path, char *ret_path)
Definition: path.c:896
void get_html_path(const char *my_exec_path, char *ret_path)
Definition: path.c:905
void get_pkginclude_path(const char *my_exec_path, char *ret_path)
Definition: path.c:851
bool has_drive_prefix(const char *path)
Definition: path.c:88
void get_includeserver_path(const char *my_exec_path, char *ret_path)
Definition: path.c:860
canonicalize_state
Definition: path.c:243
@ ABSOLUTE_WITH_N_DEPTH
Definition: path.c:246
@ RELATIVE_WITH_N_DEPTH
Definition: path.c:249
@ ABSOLUTE_PATH_INIT
Definition: path.c:244
@ RELATIVE_PATH_INIT
Definition: path.c:248
@ RELATIVE_WITH_PARENT_REF
Definition: path.c:251
#define MAXPGPATH
const void size_t len
static char * argv0
Definition: pg_ctl.c:92
static char * filename
Definition: pg_dumpall.c:119
static char * buf
Definition: pg_test_fsync.c:73
#define is_absolute_path(filename)
Definition: port.h:103
int pg_strcasecmp(const char *s1, const char *s2)
Definition: pgstrcasecmp.c:36
#define sprintf
Definition: port.h:240
bool pg_get_user_home_dir(uid_t user_id, char *buffer, size_t buflen)
Definition: user.c:64
#define snprintf
Definition: port.h:238
#define fprintf
Definition: port.h:242
unsigned char pg_tolower(unsigned char ch)
Definition: pgstrcasecmp.c:122
#define IS_DIR_SEP(ch)
Definition: port.h:102
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: strlcpy.c:45
#define EXE
Definition: port.h:154
char * s1
char * s2
Definition: regguts.h:323