summaryrefslogtreecommitdiffstats
path: root/src/util/support/path.c
blob: 7a051abc9e124d9f45ebbf073145ed45b8fb6625 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/* util/support/path.c - Portable path manipulation functions */
/*
 * Copyright (C) 2011 by the Massachusetts Institute of Technology.
 * All rights reserved.
 *
 * Export of this software from the United States of America may
 *   require a specific license from the United States Government.
 *   It is the responsibility of any person or organization contemplating
 *   export to obtain such a license before exporting.
 *
 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
 * distribute this software and its documentation for any purpose and
 * without fee is hereby granted, provided that the above copyright
 * notice appear in all copies and that both that copyright notice and
 * this permission notice appear in supporting documentation, and that
 * the name of M.I.T. not be used in advertising or publicity pertaining
 * to distribution of the software without specific, written prior
 * permission.  Furthermore if you modify this software you must label
 * your software as modified software and not distribute it in such a
 * fashion that it might be confused with the original M.I.T. software.
 * M.I.T. makes no representations about the suitability of
 * this software for any purpose.  It is provided "as is" without express
 * or implied warranty.
 */

#include <k5-platform.h>

/* For testing purposes, use a different symbol for Windows path semantics. */
#ifdef _WIN32
#define WINDOWS_PATHS
#endif

/*
 * This file implements a limited set of portable path manipulation functions.
 * When in doubt about edge cases, we follow the Python os.path semantics.
 */

#ifdef WINDOWS_PATHS
#define SEP '\\'
#define IS_SEPARATOR(c) ((c) == '\\' || (c) == '/')
#else
#define SEP '/'
#define IS_SEPARATOR(c) ((c) == '/')
#endif

/* Find the rightmost path separator in path, or NULL if there is none. */
static inline const char *
find_sep(const char *path)
{
#ifdef WINDOWS_PATHS
    const char *slash, *backslash;

    slash = strrchr(path, '/');
    backslash = strrchr(path, '\\');
    if (slash != NULL && backslash != NULL)
        return (slash > backslash) ? slash : backslash;
    else
        return (slash != NULL) ? slash : backslash;
#else
    return strrchr(path, '/');
#endif
}

/* XXX drive letter prefixes */
long
k5_path_split(const char *path, char **parent_out, char **basename_out)
{
    const char *pathstart, *sep, *pend, *bstart;
    char *parent = NULL, *basename = NULL;

    if (parent_out != NULL)
        *parent_out = NULL;
    if (basename_out != NULL)
        *basename_out = NULL;

    pathstart = path;
#ifdef WINDOWS_PATHS
    if (*path != '\0' && path[1] == ':')
        pathstart = path + 2;
#endif

    sep = find_sep(pathstart);
    if (sep != NULL) {
        bstart = sep + 1;
        /* Strip off excess separators before the one we found. */
        pend = sep;
        while (pend > pathstart && IS_SEPARATOR(pend[-1]))
            pend--;
        /* But if we hit the start, keep the whole separator sequence. */
        if (pend == pathstart)
            pend = sep + 1;
    } else {
        bstart = pathstart;
        pend = pathstart;
    }

    if (parent_out) {
        parent = malloc(pend - path + 1);
        if (parent == NULL)
            return ENOMEM;
        memcpy(parent, path, pend - path);
        parent[pend - path] = '\0';
    }
    if (basename_out) {
        basename = strdup(bstart);
        if (basename == NULL) {
            free(parent);
            return ENOMEM;
        }
    }

    if (parent_out)
        *parent_out = parent;
    if (basename_out)
        *basename_out = basename;
    return 0;
}

long
k5_path_join(const char *path1, const char *path2, char **path_out)
{
    char *path, c;
    int ret;

    *path_out = NULL;
    if (k5_path_isabs(path2) || *path1 == '\0') {
        /* Discard path1 and return a copy of path2. */
        path = strdup(path2);
        if (path == NULL)
            return ENOMEM;
    } else {
        /*
         * Compose path1 and path2, adding a separator if path1 is non-empty
         * there's no separator between them already.  (*path2 can be a
         * separator in the weird case where it starts with /: or \: on
         * Windows, and Python doesn't insert a separator in this case.)
         */
        c = path1[strlen(path1) - 1];
        if (IS_SEPARATOR(c) || IS_SEPARATOR(*path2))
            ret = asprintf(&path, "%s%s", path1, path2);
        else
            ret = asprintf(&path, "%s%c%s", path1, SEP, path2);
        if (ret < 0)
            return ENOMEM;
    }
    *path_out = path;
    return 0;
}

int
k5_path_isabs(const char *path)
{
#ifdef WINDOWS_PATHS
    if (*path != '\0' && path[1] == ':')
        path += 2;
    return (*path == '/' || *path == '\\');
#else
    return (*path == '/');
#endif
}