~vcs-imports/mammoth-replicator/trunk

« back to all changes in this revision

Viewing changes to src/port/path.c

  • Committer: alvherre
  • Date: 2005-12-16 21:24:52 UTC
  • Revision ID: svn-v4:db760fc0-0f08-0410-9d63-cc6633f64896:trunk:1
Initial import of the REL8_0_3 sources from the Pgsql CVS repository.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*-------------------------------------------------------------------------
 
2
 *
 
3
 * path.c
 
4
 *        portable path handling routines
 
5
 *
 
6
 * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
 
7
 * Portions Copyright (c) 1994, Regents of the University of California
 
8
 *
 
9
 *
 
10
 * IDENTIFICATION
 
11
 *        $PostgreSQL: pgsql/src/port/path.c,v 1.50.4.1 2005-01-26 19:24:21 tgl Exp $
 
12
 *
 
13
 *-------------------------------------------------------------------------
 
14
 */
 
15
 
 
16
#include "c.h"
 
17
 
 
18
#include <ctype.h>
 
19
#include <sys/stat.h>
 
20
#ifdef WIN32
 
21
#ifdef _WIN32_IE
 
22
#undef _WIN32_IE
 
23
#endif
 
24
#define _WIN32_IE 0x0500
 
25
#ifdef near
 
26
#undef near
 
27
#endif
 
28
#define near
 
29
#include <shlobj.h>
 
30
#else
 
31
#include <unistd.h>
 
32
#endif
 
33
 
 
34
#include "pg_config_paths.h"
 
35
 
 
36
 
 
37
#ifndef WIN32
 
38
#define IS_DIR_SEP(ch)  ((ch) == '/')
 
39
#else
 
40
#define IS_DIR_SEP(ch)  ((ch) == '/' || (ch) == '\\')
 
41
#endif
 
42
 
 
43
#ifndef WIN32
 
44
#define IS_PATH_SEP(ch) ((ch) == ':')
 
45
#else
 
46
#define IS_PATH_SEP(ch) ((ch) == ';')
 
47
#endif
 
48
 
 
49
static void make_relative_path(char *ret_path, const char *target_path,
 
50
                                                           const char *bin_path, const char *my_exec_path);
 
51
static void trim_directory(char *path);
 
52
static void trim_trailing_separator(char *path);
 
53
 
 
54
 
 
55
/*
 
56
 * skip_drive
 
57
 *
 
58
 * On Windows, a path may begin with "C:" or "//network/".  Advance over
 
59
 * this and point to the effective start of the path.
 
60
 */
 
61
#ifdef WIN32
 
62
 
 
63
static char *
 
64
skip_drive(const char *path)
 
65
{
 
66
        if (IS_DIR_SEP(path[0]) && IS_DIR_SEP(path[1]))
 
67
        {
 
68
                path += 2;
 
69
                while (*path && !IS_DIR_SEP(*path))
 
70
                        path++;
 
71
        }
 
72
        else if (isalpha(path[0]) && path[1] == ':')
 
73
        {
 
74
                path += 2;
 
75
        }
 
76
        return (char *) path;
 
77
}
 
78
 
 
79
#else
 
80
 
 
81
#define skip_drive(path)        (path)
 
82
 
 
83
#endif
 
84
 
 
85
/*
 
86
 *      first_dir_separator
 
87
 *
 
88
 * Find the location of the first directory separator, return
 
89
 * NULL if not found.
 
90
 */
 
91
char *
 
92
first_dir_separator(const char *filename)
 
93
{
 
94
        const char *p;
 
95
 
 
96
        for (p = skip_drive(filename); *p; p++)
 
97
                if (IS_DIR_SEP(*p))
 
98
                        return (char *) p;
 
99
        return NULL;
 
100
}
 
101
 
 
102
/*
 
103
 *      first_path_separator
 
104
 *
 
105
 * Find the location of the first path separator (i.e. ':' on
 
106
 * Unix, ';' on Windows), return NULL if not found.
 
107
 */
 
108
char *
 
109
first_path_separator(const char *pathlist)
 
110
{
 
111
        const char *p;
 
112
 
 
113
        /* skip_drive is not needed */
 
114
        for (p = pathlist; *p; p++)
 
115
                if (IS_PATH_SEP(*p))
 
116
                        return (char *) p;
 
117
        return NULL;
 
118
}
 
119
 
 
120
/*
 
121
 *      last_dir_separator
 
122
 *
 
123
 * Find the location of the last directory separator, return
 
124
 * NULL if not found.
 
125
 */
 
126
char *
 
127
last_dir_separator(const char *filename)
 
128
{
 
129
        const char *p,
 
130
                           *ret = NULL;
 
131
 
 
132
        for (p = skip_drive(filename); *p; p++)
 
133
                if (IS_DIR_SEP(*p))
 
134
                        ret = p;
 
135
        return (char *) ret;
 
136
}
 
137
 
 
138
 
 
139
/*
 
140
 *      make_native_path - on WIN32, change / to \ in the path
 
141
 *
 
142
 *      This effectively undoes canonicalize_path.
 
143
 *
 
144
 *      This is required because WIN32 COPY is an internal CMD.EXE
 
145
 *      command and doesn't process forward slashes in the same way
 
146
 *      as external commands.  Quoting the first argument to COPY
 
147
 *      does not convert forward to backward slashes, but COPY does
 
148
 *      properly process quoted forward slashes in the second argument.
 
149
 *
 
150
 *      COPY works with quoted forward slashes in the first argument
 
151
 *      only if the current directory is the same as the directory
 
152
 *      of the first argument.
 
153
 */
 
154
void
 
155
make_native_path(char *filename)
 
156
{
 
157
#ifdef WIN32
 
158
        char       *p;
 
159
 
 
160
        for (p = filename; *p; p++)
 
161
                if (*p == '/')
 
162
                        *p = '\\';
 
163
#endif
 
164
}
 
165
 
 
166
 
 
167
/*
 
168
 * join_path_components - join two path components, inserting a slash
 
169
 *
 
170
 * ret_path is the output area (must be of size MAXPGPATH)
 
171
 *
 
172
 * ret_path can be the same as head, but not the same as tail.
 
173
 */
 
174
void
 
175
join_path_components(char *ret_path,
 
176
                                         const char *head, const char *tail)
 
177
{
 
178
        if (ret_path != head)
 
179
                StrNCpy(ret_path, head, MAXPGPATH);
 
180
        /*
 
181
         * Remove any leading "." and ".." in the tail component,
 
182
         * adjusting head as needed.
 
183
         */
 
184
        for (;;)
 
185
        {
 
186
                if (tail[0] == '.' && IS_DIR_SEP(tail[1]))
 
187
                {
 
188
                        tail += 2;
 
189
                }
 
190
                else if (tail[0] == '.' && tail[1] == '\0')
 
191
                {
 
192
                        tail += 1;
 
193
                        break;
 
194
                }
 
195
                else if (tail[0] == '.' && tail[1] == '.' && IS_DIR_SEP(tail[2]))
 
196
                {
 
197
                        trim_directory(ret_path);
 
198
                        tail += 3;
 
199
                }
 
200
                else if (tail[0] == '.' && tail[1] == '.' && tail[2] == '\0')
 
201
                {
 
202
                        trim_directory(ret_path);
 
203
                        tail += 2;
 
204
                        break;
 
205
                }
 
206
                else
 
207
                        break;
 
208
        }
 
209
        if (*tail)
 
210
                snprintf(ret_path + strlen(ret_path), MAXPGPATH - strlen(ret_path),
 
211
                                 "/%s", tail);
 
212
}
 
213
 
 
214
 
 
215
/*
 
216
 *      Clean up path by:
 
217
 *              o  make Win32 path use Unix slashes
 
218
 *              o  remove trailing quote on Win32
 
219
 *              o  remove trailing slash
 
220
 *              o  remove duplicate adjacent separators
 
221
 *              o  remove trailing '.'
 
222
 *              o  process trailing '..' ourselves
 
223
 */
 
224
void
 
225
canonicalize_path(char *path)
 
226
{
 
227
        char       *p, *to_p;
 
228
        bool            was_sep = false;
 
229
 
 
230
#ifdef WIN32
 
231
        /*
 
232
         * The Windows command processor will accept suitably quoted paths
 
233
         * with forward slashes, but barfs badly with mixed forward and back
 
234
         * slashes.
 
235
         */
 
236
        for (p = path; *p; p++)
 
237
        {
 
238
                if (*p == '\\')
 
239
                        *p = '/';
 
240
        }
 
241
 
 
242
        /*
 
243
         * In Win32, if you do: prog.exe "a b" "\c\d\" the system will pass
 
244
         * \c\d" as argv[2], so trim off trailing quote.
 
245
         */
 
246
        if (p > path && *(p - 1) == '"')
 
247
                *(p - 1) = '/';
 
248
#endif
 
249
 
 
250
        /*
 
251
         * Removing the trailing slash on a path means we never get ugly
 
252
         * double trailing slashes.     Also, Win32 can't stat() a directory
 
253
         * with a trailing slash. Don't remove a leading slash, though.
 
254
         */
 
255
        trim_trailing_separator(path);
 
256
 
 
257
        /*
 
258
         *      Remove duplicate adjacent separators
 
259
         */
 
260
        p = path;
 
261
#ifdef WIN32
 
262
        /* Don't remove leading double-slash on Win32 */
 
263
        if (*p)
 
264
                p++;
 
265
#endif
 
266
        to_p = p;
 
267
        for (; *p; p++, to_p++)
 
268
        {
 
269
                /* Handle many adjacent slashes, like "/a///b" */
 
270
                while (*p == '/' && was_sep)
 
271
                        p++;
 
272
                if (to_p != p)
 
273
                        *to_p = *p;
 
274
                was_sep = (*p == '/');
 
275
        }
 
276
        *to_p = '\0';
 
277
 
 
278
        /*
 
279
         * Remove any trailing uses of "." and process ".." ourselves
 
280
         */
 
281
        for (;;)
 
282
        {
 
283
                int                     len = strlen(path);
 
284
 
 
285
                if (len > 2 && strcmp(path + len - 2, "/.") == 0)
 
286
                        trim_directory(path);
 
287
                else if (len > 3 && strcmp(path + len - 3, "/..") == 0)
 
288
                {
 
289
                        trim_directory(path);
 
290
                        trim_directory(path);   /* remove directory above */
 
291
                }
 
292
                else
 
293
                        break;
 
294
        }
 
295
}
 
296
 
 
297
 
 
298
/*
 
299
 * Extracts the actual name of the program as called - 
 
300
 * stripped of .exe suffix if any
 
301
 */
 
302
const char *
 
303
get_progname(const char *argv0)
 
304
{
 
305
        const char *nodir_name;
 
306
 
 
307
        nodir_name = last_dir_separator(argv0);
 
308
        if (nodir_name)
 
309
                nodir_name++;
 
310
        else
 
311
                nodir_name = skip_drive(argv0);
 
312
 
 
313
#if defined(__CYGWIN__) || defined(WIN32)
 
314
        /* strip .exe suffix, regardless of case */
 
315
        if (strlen(nodir_name) > sizeof(EXE) - 1 &&
 
316
                pg_strcasecmp(nodir_name + strlen(nodir_name)-(sizeof(EXE)-1), EXE) == 0)
 
317
        {
 
318
                char *progname;
 
319
 
 
320
                progname = strdup(nodir_name);
 
321
                if (progname == NULL)
 
322
                {
 
323
                        fprintf(stderr, "%s: out of memory\n", nodir_name);
 
324
                        exit(1);        /* This could exit the postmaster */
 
325
                }
 
326
                progname[strlen(progname) - (sizeof(EXE) - 1)] = '\0';
 
327
                nodir_name = progname; 
 
328
        }
 
329
#endif
 
330
 
 
331
        return nodir_name;
 
332
}
 
333
 
 
334
 
 
335
/*
 
336
 * make_relative_path - make a path relative to the actual binary location
 
337
 *
 
338
 * This function exists to support relocation of installation trees.
 
339
 *
 
340
 *      ret_path is the output area (must be of size MAXPGPATH)
 
341
 *      target_path is the compiled-in path to the directory we want to find
 
342
 *      bin_path is the compiled-in path to the directory of executables
 
343
 *      my_exec_path is the actual location of my executable
 
344
 *
 
345
 * If target_path matches bin_path up to the last directory component of
 
346
 * bin_path, then we build the result as my_exec_path (less the executable
 
347
 * name and last directory) joined to the non-matching part of target_path.
 
348
 * Otherwise, we return target_path as-is.
 
349
 * 
 
350
 * For example:
 
351
 *              target_path  = '/usr/local/share/postgresql'
 
352
 *              bin_path     = '/usr/local/bin'
 
353
 *              my_exec_path = '/opt/pgsql/bin/postmaster'
 
354
 * Given these inputs we would return '/opt/pgsql/share/postgresql'
 
355
 */
 
356
static void
 
357
make_relative_path(char *ret_path, const char *target_path,
 
358
                                   const char *bin_path, const char *my_exec_path)
 
359
{
 
360
        const char *bin_end;
 
361
        int                     prefix_len;
 
362
 
 
363
        bin_end = last_dir_separator(bin_path);
 
364
        if (!bin_end)
 
365
                goto no_match;
 
366
        prefix_len = bin_end - bin_path + 1;
 
367
        if (strncmp(target_path, bin_path, prefix_len) != 0)
 
368
                goto no_match;
 
369
 
 
370
        StrNCpy(ret_path, my_exec_path, MAXPGPATH);
 
371
        trim_directory(ret_path);       /* remove my executable name */
 
372
        trim_directory(ret_path);       /* remove last directory component (/bin) */
 
373
        join_path_components(ret_path, ret_path, target_path + prefix_len);
 
374
        canonicalize_path(ret_path);
 
375
        return;
 
376
 
 
377
no_match:
 
378
        StrNCpy(ret_path, target_path, MAXPGPATH);
 
379
        canonicalize_path(ret_path);
 
380
}
 
381
 
 
382
 
 
383
/*
 
384
 *      get_share_path
 
385
 */
 
386
void
 
387
get_share_path(const char *my_exec_path, char *ret_path)
 
388
{
 
389
        make_relative_path(ret_path, PGSHAREDIR, PGBINDIR, my_exec_path);
 
390
}
 
391
 
 
392
/*
 
393
 *      get_etc_path
 
394
 */
 
395
void
 
396
get_etc_path(const char *my_exec_path, char *ret_path)
 
397
{
 
398
        make_relative_path(ret_path, SYSCONFDIR, PGBINDIR, my_exec_path);
 
399
}
 
400
 
 
401
/*
 
402
 *      get_include_path
 
403
 */
 
404
void
 
405
get_include_path(const char *my_exec_path, char *ret_path)
 
406
{
 
407
        make_relative_path(ret_path, INCLUDEDIR, PGBINDIR, my_exec_path);
 
408
}
 
409
 
 
410
/*
 
411
 *      get_pkginclude_path
 
412
 */
 
413
void
 
414
get_pkginclude_path(const char *my_exec_path, char *ret_path)
 
415
{
 
416
        make_relative_path(ret_path, PKGINCLUDEDIR, PGBINDIR, my_exec_path);
 
417
}
 
418
 
 
419
/*
 
420
 *      get_includeserver_path
 
421
 */
 
422
void
 
423
get_includeserver_path(const char *my_exec_path, char *ret_path)
 
424
{
 
425
        make_relative_path(ret_path, INCLUDEDIRSERVER, PGBINDIR, my_exec_path);
 
426
}
 
427
 
 
428
/*
 
429
 *      get_lib_path
 
430
 */
 
431
void
 
432
get_lib_path(const char *my_exec_path, char *ret_path)
 
433
{
 
434
        make_relative_path(ret_path, LIBDIR, PGBINDIR, my_exec_path);
 
435
}
 
436
 
 
437
/*
 
438
 *      get_pkglib_path
 
439
 */
 
440
void
 
441
get_pkglib_path(const char *my_exec_path, char *ret_path)
 
442
{
 
443
        make_relative_path(ret_path, PKGLIBDIR, PGBINDIR, my_exec_path);
 
444
}
 
445
 
 
446
/*
 
447
 *      get_locale_path
 
448
 */
 
449
void
 
450
get_locale_path(const char *my_exec_path, char *ret_path)
 
451
{
 
452
        make_relative_path(ret_path, LOCALEDIR, PGBINDIR, my_exec_path);
 
453
}
 
454
 
 
455
 
 
456
/*
 
457
 *      get_home_path
 
458
 *
 
459
 * On Unix, this actually returns the user's home directory.  On Windows
 
460
 * it returns the PostgreSQL-specific application data folder.
 
461
 */
 
462
bool
 
463
get_home_path(char *ret_path)
 
464
{
 
465
#ifndef WIN32
 
466
        char            pwdbuf[BUFSIZ];
 
467
        struct passwd pwdstr;
 
468
        struct passwd *pwd = NULL;
 
469
 
 
470
        if (pqGetpwuid(geteuid(), &pwdstr, pwdbuf, sizeof(pwdbuf), &pwd) != 0)
 
471
                return false;
 
472
        StrNCpy(ret_path, pwd->pw_dir, MAXPGPATH);
 
473
        return true;
 
474
 
 
475
#else
 
476
        char            tmppath[MAX_PATH];
 
477
 
 
478
        ZeroMemory(tmppath, sizeof(tmppath));
 
479
        if (SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, 0, tmppath) != S_OK)
 
480
                return false;
 
481
        snprintf(ret_path, MAXPGPATH, "%s/postgresql", tmppath);
 
482
        return true;
 
483
#endif
 
484
}
 
485
 
 
486
 
 
487
/*
 
488
 * get_parent_directory
 
489
 *
 
490
 * Modify the given string in-place to name the parent directory of the
 
491
 * named file.
 
492
 */
 
493
void
 
494
get_parent_directory(char *path)
 
495
{
 
496
        trim_directory(path);
 
497
}
 
498
 
 
499
 
 
500
/*
 
501
 *      set_pglocale_pgservice
 
502
 *
 
503
 *      Set application-specific locale and service directory
 
504
 *
 
505
 *      This function takes an argv[0] rather than a full path.
 
506
 */
 
507
void
 
508
set_pglocale_pgservice(const char *argv0, const char *app)
 
509
{
 
510
        char            path[MAXPGPATH];
 
511
        char            my_exec_path[MAXPGPATH];
 
512
        char            env_path[MAXPGPATH + sizeof("PGSYSCONFDIR=")];  /* longer than
 
513
                                                                                                                                 * PGLOCALEDIR */
 
514
 
 
515
        /* don't set LC_ALL in the backend */
 
516
        if (strcmp(app, "postgres") != 0)
 
517
                setlocale(LC_ALL, "");
 
518
 
 
519
        if (find_my_exec(argv0, my_exec_path) < 0)
 
520
                return;
 
521
 
 
522
#ifdef ENABLE_NLS
 
523
        get_locale_path(my_exec_path, path);
 
524
        bindtextdomain(app, path);
 
525
        textdomain(app);
 
526
 
 
527
        if (getenv("PGLOCALEDIR") == NULL)
 
528
        {
 
529
                /* set for libpq to use */
 
530
                snprintf(env_path, sizeof(env_path), "PGLOCALEDIR=%s", path);
 
531
                canonicalize_path(env_path + 12);
 
532
                putenv(strdup(env_path));
 
533
        }
 
534
#endif
 
535
 
 
536
        if (getenv("PGSYSCONFDIR") == NULL)
 
537
        {
 
538
                get_etc_path(my_exec_path, path);
 
539
 
 
540
                /* set for libpq to use */
 
541
                snprintf(env_path, sizeof(env_path), "PGSYSCONFDIR=%s", path);
 
542
                canonicalize_path(env_path + 13);
 
543
                putenv(strdup(env_path));
 
544
        }
 
545
}
 
546
 
 
547
 
 
548
/*
 
549
 *      trim_directory
 
550
 *
 
551
 *      Trim trailing directory from path, that is, remove any trailing slashes,
 
552
 *      the last pathname component, and the slash just ahead of it --- but never
 
553
 *      remove a leading slash.
 
554
 */
 
555
static void
 
556
trim_directory(char *path)
 
557
{
 
558
        char       *p;
 
559
 
 
560
        path = skip_drive(path);
 
561
 
 
562
        if (path[0] == '\0')
 
563
                return;
 
564
 
 
565
        /* back up over trailing slash(es) */
 
566
        for (p = path + strlen(path) - 1; IS_DIR_SEP(*p) && p > path; p--)
 
567
                ;
 
568
        /* back up over directory name */
 
569
        for (; !IS_DIR_SEP(*p) && p > path; p--)
 
570
                ;
 
571
        /* if multiple slashes before directory name, remove 'em all */
 
572
        for (; p > path && IS_DIR_SEP(*(p - 1)); p--)
 
573
                ;
 
574
        /* don't erase a leading slash */
 
575
        if (p == path && IS_DIR_SEP(*p))
 
576
                p++;
 
577
        *p = '\0';
 
578
}
 
579
 
 
580
 
 
581
/*
 
582
 *      trim_trailing_separator
 
583
 *
 
584
 * trim off trailing slashes, but not a leading slash
 
585
 */
 
586
static void
 
587
trim_trailing_separator(char *path)
 
588
{
 
589
        char       *p;
 
590
 
 
591
        path = skip_drive(path);
 
592
        p = path + strlen(path);
 
593
        if (p > path)
 
594
                for (p--; p > path && IS_DIR_SEP(*p); p--)
 
595
                        *p = '\0';
 
596
}