YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/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
}