xref: /freebsd/crypto/krb5/src/util/support/path.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
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