bba2c231d0896a10d8bd81984cdb4cb35b772974
[platform/upstream/p11-kit.git] / common / path.c
1 /*
2  * Copyright (c) 2005 Stefan Walter
3  * Copyright (c) 2011 Collabora Ltd.
4  * Copyright (c) 2013 Red Hat Inc.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  *     * Redistributions of source code must retain the above
11  *       copyright notice, this list of conditions and the
12  *       following disclaimer.
13  *     * Redistributions in binary form must reproduce the
14  *       above copyright notice, this list of conditions and
15  *       the following disclaimer in the documentation and/or
16  *       other materials provided with the distribution.
17  *     * The names of contributors to this software may not be
18  *       used to endorse or promote products derived from this
19  *       software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
28  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
31  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
32  * DAMAGE.
33  *
34  *
35  * CONTRIBUTORS
36  *  Stef Walter <stefw@redhat.com>
37  */
38
39 #include "config.h"
40
41 #include "debug.h"
42 #include "message.h"
43 #include "path.h"
44
45 #include <assert.h>
46 #include <errno.h>
47 #include <stdarg.h>
48 #include <stdlib.h>
49 #include <string.h>
50
51 #ifdef OS_UNIX
52 #include <paths.h>
53 #include <pwd.h>
54 #include <unistd.h>
55 #endif
56
57 #ifdef OS_WIN32
58 #include <shlobj.h>
59 #endif
60
61
62 char *
63 p11_path_base (const char *path)
64 {
65 #ifdef OS_WIN32
66         static const char *delims = "/\\";
67 #else
68         static const char *delims = "/";
69 #endif
70
71         const char *end;
72         const char *beg;
73
74         return_val_if_fail (path != NULL, NULL);
75
76         /* Any trailing slashes */
77         end = path + strlen (path);
78         while (end != path) {
79                 if (!strchr (delims, *(end - 1)))
80                         break;
81                 end--;
82         }
83
84         /* Find the last slash after those */
85         beg = end;
86         while (beg != path) {
87                 if (strchr (delims, *(beg - 1)))
88                         break;
89                 beg--;
90         }
91
92         return strndup (beg, end - beg);
93 }
94
95 static char *
96 expand_homedir (const char *remainder)
97 {
98         const char *env;
99
100         env = getenv ("HOME");
101         if (env && env[0]) {
102                 return p11_path_build (env, remainder, NULL);
103
104         } else {
105 #ifdef OS_UNIX
106                 struct passwd *pwd;
107                 int error = 0;
108
109                 pwd = getpwuid (getuid ());
110                 if (!pwd) {
111                         error = errno;
112                         p11_message ("couldn't lookup home directory for user %d: %s",
113                                      getuid (), strerror (errno));
114                         errno = error;
115                         return NULL;
116                 }
117
118                 return p11_path_build (pwd->pw_dir, remainder, NULL);
119
120 #else /* OS_WIN32 */
121                 char directory[MAX_PATH + 1];
122
123                 if (!SHGetSpecialFolderPathA (NULL, directory, CSIDL_PROFILE, TRUE)) {
124                         p11_message ("couldn't lookup home directory for user");
125                         errno = ENOTDIR;
126                         return NULL;
127                 }
128
129                 return p11_path_build (directory, remainder, NULL);
130
131 #endif /* OS_WIN32 */
132         }
133 }
134
135 static char *
136 expand_tempdir (const char *remainder)
137 {
138         const char *env;
139
140         env = getenv ("TEMP");
141         if (env && env[0]) {
142                 return p11_path_build (env, remainder, NULL);
143
144         } else {
145 #ifdef OS_UNIX
146 #ifdef _PATH_TMP
147                 return p11_path_build (_PATH_TMP, remainder, NULL);
148 #else
149                 return p11_path_build ("/tmp", remainder, NULL);
150 #endif
151
152 #else /* OS_WIN32 */
153                 char directory[MAX_PATH + 1];
154
155                 if (!GetTempPathA (MAX_PATH + 1, directory)) {
156                         p11_message ("couldn't lookup temp directory");
157                         errno = ENOTDIR;
158                         return NULL;
159                 }
160
161                 return p11_path_build (directory, remainder, NULL);
162
163 #endif /* OS_WIN32 */
164         }
165 }
166
167 static bool
168 is_path_component_or_null (char ch)
169 {
170         return (ch == '0' || ch == '/'
171 #ifdef OS_WIN32
172                         || ch == '\\'
173 #endif
174                 );
175 }
176
177 char *
178 p11_path_expand (const char *path)
179 {
180         return_val_if_fail (path != NULL, NULL);
181
182         if (strncmp (path, "~", 1) == 0 &&
183             is_path_component_or_null (path[1])) {
184                 return expand_homedir (path + 2);
185
186         } else if (strncmp (path, "$HOME", 5) == 0 &&
187             is_path_component_or_null (path[5])) {
188                 return expand_homedir (path + 6);
189
190         } else if (strncmp (path, "$TEMP", 5) == 0 &&
191             is_path_component_or_null (path[5])) {
192                 return expand_tempdir (path + 6);
193
194         } else {
195                 return strdup (path);
196         }
197 }
198
199 bool
200 p11_path_absolute (const char *path)
201 {
202         return_val_if_fail (path != NULL, false);
203
204 #ifdef OS_UNIX
205         return (path[0] == '/');
206 #else
207         return (path[0] != '\0' && path[1] == ':' && path[2] == '\\');
208 #endif
209 }
210
211 char *
212 p11_path_build (const char *path,
213                 ...)
214 {
215 #ifdef OS_WIN32
216         static const char delim = '\\';
217 #else
218         static const char delim = '/';
219 #endif
220         const char *first = path;
221         char *built;
222         size_t len;
223         size_t at;
224         size_t num;
225         va_list va;
226
227         return_val_if_fail (path != NULL, NULL);
228
229         len = 1;
230         va_start (va, path);
231         while (path != NULL) {
232                 len += strlen (path) + 1;
233                 path = va_arg (va, const char *);
234         }
235         va_end (va);
236
237         built = malloc (len + 1);
238         return_val_if_fail (built != NULL, NULL);
239
240         at = 0;
241         path = first;
242         va_start (va, path);
243         while (path != NULL) {
244                 if (at != 0 && built[at - 1] != delim && path[0] != delim)
245                         built[at++] = delim;
246                 num = strlen (path);
247                 assert (at + num < len);
248                 memcpy (built + at, path, num);
249
250                 at += num;
251                 path = va_arg (va, const char *);
252         }
253         va_end (va);
254
255         assert (at < len);
256         built[at] = '\0';
257         return built;
258 }