1*7f2fe78bSCy Schubert /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2*7f2fe78bSCy Schubert /* util/support/path.c - Portable path manipulation functions */
3*7f2fe78bSCy Schubert /*
4*7f2fe78bSCy Schubert * Copyright (C) 2011 by the Massachusetts Institute of Technology.
5*7f2fe78bSCy Schubert * All rights reserved.
6*7f2fe78bSCy Schubert *
7*7f2fe78bSCy Schubert * Export of this software from the United States of America may
8*7f2fe78bSCy Schubert * require a specific license from the United States Government.
9*7f2fe78bSCy Schubert * It is the responsibility of any person or organization contemplating
10*7f2fe78bSCy Schubert * export to obtain such a license before exporting.
11*7f2fe78bSCy Schubert *
12*7f2fe78bSCy Schubert * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13*7f2fe78bSCy Schubert * distribute this software and its documentation for any purpose and
14*7f2fe78bSCy Schubert * without fee is hereby granted, provided that the above copyright
15*7f2fe78bSCy Schubert * notice appear in all copies and that both that copyright notice and
16*7f2fe78bSCy Schubert * this permission notice appear in supporting documentation, and that
17*7f2fe78bSCy Schubert * the name of M.I.T. not be used in advertising or publicity pertaining
18*7f2fe78bSCy Schubert * to distribution of the software without specific, written prior
19*7f2fe78bSCy Schubert * permission. Furthermore if you modify this software you must label
20*7f2fe78bSCy Schubert * your software as modified software and not distribute it in such a
21*7f2fe78bSCy Schubert * fashion that it might be confused with the original M.I.T. software.
22*7f2fe78bSCy Schubert * M.I.T. makes no representations about the suitability of
23*7f2fe78bSCy Schubert * this software for any purpose. It is provided "as is" without express
24*7f2fe78bSCy Schubert * or implied warranty.
25*7f2fe78bSCy Schubert */
26*7f2fe78bSCy Schubert
27*7f2fe78bSCy Schubert #include <k5-platform.h>
28*7f2fe78bSCy Schubert
29*7f2fe78bSCy Schubert /* For testing purposes, use a different symbol for Windows path semantics. */
30*7f2fe78bSCy Schubert #ifdef _WIN32
31*7f2fe78bSCy Schubert #define WINDOWS_PATHS
32*7f2fe78bSCy Schubert #endif
33*7f2fe78bSCy Schubert
34*7f2fe78bSCy Schubert /*
35*7f2fe78bSCy Schubert * This file implements a limited set of portable path manipulation functions.
36*7f2fe78bSCy Schubert * When in doubt about edge cases, we follow the Python os.path semantics.
37*7f2fe78bSCy Schubert */
38*7f2fe78bSCy Schubert
39*7f2fe78bSCy Schubert #ifdef WINDOWS_PATHS
40*7f2fe78bSCy Schubert #define SEP '\\'
41*7f2fe78bSCy Schubert #define IS_SEPARATOR(c) ((c) == '\\' || (c) == '/')
42*7f2fe78bSCy Schubert #else
43*7f2fe78bSCy Schubert #define SEP '/'
44*7f2fe78bSCy Schubert #define IS_SEPARATOR(c) ((c) == '/')
45*7f2fe78bSCy Schubert #endif
46*7f2fe78bSCy Schubert
47*7f2fe78bSCy Schubert /* Find the rightmost path separator in path, or NULL if there is none. */
48*7f2fe78bSCy Schubert static inline const char *
find_sep(const char * path)49*7f2fe78bSCy Schubert find_sep(const char *path)
50*7f2fe78bSCy Schubert {
51*7f2fe78bSCy Schubert #ifdef WINDOWS_PATHS
52*7f2fe78bSCy Schubert const char *slash, *backslash;
53*7f2fe78bSCy Schubert
54*7f2fe78bSCy Schubert slash = strrchr(path, '/');
55*7f2fe78bSCy Schubert backslash = strrchr(path, '\\');
56*7f2fe78bSCy Schubert if (slash != NULL && backslash != NULL)
57*7f2fe78bSCy Schubert return (slash > backslash) ? slash : backslash;
58*7f2fe78bSCy Schubert else
59*7f2fe78bSCy Schubert return (slash != NULL) ? slash : backslash;
60*7f2fe78bSCy Schubert #else
61*7f2fe78bSCy Schubert return strrchr(path, '/');
62*7f2fe78bSCy Schubert #endif
63*7f2fe78bSCy Schubert }
64*7f2fe78bSCy Schubert
65*7f2fe78bSCy Schubert /* XXX drive letter prefixes */
66*7f2fe78bSCy Schubert long
k5_path_split(const char * path,char ** parent_out,char ** basename_out)67*7f2fe78bSCy Schubert k5_path_split(const char *path, char **parent_out, char **basename_out)
68*7f2fe78bSCy Schubert {
69*7f2fe78bSCy Schubert const char *pathstart, *sep, *pend, *bstart;
70*7f2fe78bSCy Schubert char *parent = NULL, *basename = NULL;
71*7f2fe78bSCy Schubert
72*7f2fe78bSCy Schubert if (parent_out != NULL)
73*7f2fe78bSCy Schubert *parent_out = NULL;
74*7f2fe78bSCy Schubert if (basename_out != NULL)
75*7f2fe78bSCy Schubert *basename_out = NULL;
76*7f2fe78bSCy Schubert
77*7f2fe78bSCy Schubert pathstart = path;
78*7f2fe78bSCy Schubert #ifdef WINDOWS_PATHS
79*7f2fe78bSCy Schubert if (*path != '\0' && path[1] == ':')
80*7f2fe78bSCy Schubert pathstart = path + 2;
81*7f2fe78bSCy Schubert #endif
82*7f2fe78bSCy Schubert
83*7f2fe78bSCy Schubert sep = find_sep(pathstart);
84*7f2fe78bSCy Schubert if (sep != NULL) {
85*7f2fe78bSCy Schubert bstart = sep + 1;
86*7f2fe78bSCy Schubert /* Strip off excess separators before the one we found. */
87*7f2fe78bSCy Schubert pend = sep;
88*7f2fe78bSCy Schubert while (pend > pathstart && IS_SEPARATOR(pend[-1]))
89*7f2fe78bSCy Schubert pend--;
90*7f2fe78bSCy Schubert /* But if we hit the start, keep the whole separator sequence. */
91*7f2fe78bSCy Schubert if (pend == pathstart)
92*7f2fe78bSCy Schubert pend = sep + 1;
93*7f2fe78bSCy Schubert } else {
94*7f2fe78bSCy Schubert bstart = pathstart;
95*7f2fe78bSCy Schubert pend = pathstart;
96*7f2fe78bSCy Schubert }
97*7f2fe78bSCy Schubert
98*7f2fe78bSCy Schubert if (parent_out) {
99*7f2fe78bSCy Schubert parent = malloc(pend - path + 1);
100*7f2fe78bSCy Schubert if (parent == NULL)
101*7f2fe78bSCy Schubert return ENOMEM;
102*7f2fe78bSCy Schubert memcpy(parent, path, pend - path);
103*7f2fe78bSCy Schubert parent[pend - path] = '\0';
104*7f2fe78bSCy Schubert }
105*7f2fe78bSCy Schubert if (basename_out) {
106*7f2fe78bSCy Schubert basename = strdup(bstart);
107*7f2fe78bSCy Schubert if (basename == NULL) {
108*7f2fe78bSCy Schubert free(parent);
109*7f2fe78bSCy Schubert return ENOMEM;
110*7f2fe78bSCy Schubert }
111*7f2fe78bSCy Schubert }
112*7f2fe78bSCy Schubert
113*7f2fe78bSCy Schubert if (parent_out)
114*7f2fe78bSCy Schubert *parent_out = parent;
115*7f2fe78bSCy Schubert if (basename_out)
116*7f2fe78bSCy Schubert *basename_out = basename;
117*7f2fe78bSCy Schubert return 0;
118*7f2fe78bSCy Schubert }
119*7f2fe78bSCy Schubert
120*7f2fe78bSCy Schubert long
k5_path_join(const char * path1,const char * path2,char ** path_out)121*7f2fe78bSCy Schubert k5_path_join(const char *path1, const char *path2, char **path_out)
122*7f2fe78bSCy Schubert {
123*7f2fe78bSCy Schubert char *path, c;
124*7f2fe78bSCy Schubert int ret;
125*7f2fe78bSCy Schubert
126*7f2fe78bSCy Schubert *path_out = NULL;
127*7f2fe78bSCy Schubert if (k5_path_isabs(path2) || *path1 == '\0') {
128*7f2fe78bSCy Schubert /* Discard path1 and return a copy of path2. */
129*7f2fe78bSCy Schubert path = strdup(path2);
130*7f2fe78bSCy Schubert if (path == NULL)
131*7f2fe78bSCy Schubert return ENOMEM;
132*7f2fe78bSCy Schubert } else {
133*7f2fe78bSCy Schubert /*
134*7f2fe78bSCy Schubert * Compose path1 and path2, adding a separator if path1 is non-empty
135*7f2fe78bSCy Schubert * there's no separator between them already. (*path2 can be a
136*7f2fe78bSCy Schubert * separator in the weird case where it starts with /: or \: on
137*7f2fe78bSCy Schubert * Windows, and Python doesn't insert a separator in this case.)
138*7f2fe78bSCy Schubert */
139*7f2fe78bSCy Schubert c = path1[strlen(path1) - 1];
140*7f2fe78bSCy Schubert if (IS_SEPARATOR(c) || IS_SEPARATOR(*path2))
141*7f2fe78bSCy Schubert ret = asprintf(&path, "%s%s", path1, path2);
142*7f2fe78bSCy Schubert else
143*7f2fe78bSCy Schubert ret = asprintf(&path, "%s%c%s", path1, SEP, path2);
144*7f2fe78bSCy Schubert if (ret < 0)
145*7f2fe78bSCy Schubert return ENOMEM;
146*7f2fe78bSCy Schubert }
147*7f2fe78bSCy Schubert *path_out = path;
148*7f2fe78bSCy Schubert return 0;
149*7f2fe78bSCy Schubert }
150*7f2fe78bSCy Schubert
151*7f2fe78bSCy Schubert int
k5_path_isabs(const char * path)152*7f2fe78bSCy Schubert k5_path_isabs(const char *path)
153*7f2fe78bSCy Schubert {
154*7f2fe78bSCy Schubert #ifdef WINDOWS_PATHS
155*7f2fe78bSCy Schubert if (*path != '\0' && path[1] == ':')
156*7f2fe78bSCy Schubert path += 2;
157*7f2fe78bSCy Schubert return (*path == '/' || *path == '\\');
158*7f2fe78bSCy Schubert #else
159*7f2fe78bSCy Schubert return (*path == '/');
160*7f2fe78bSCy Schubert #endif
161*7f2fe78bSCy Schubert }
162