2 * Copyright (C) the libgit2 contributors. All rights reserved.
4 * This file is part of libgit2, distributed under the GNU GPL v2 with
5 * a Linking Exception. For full terms see the included COPYING file.
15 #include "win32/findfile.h"
21 static int git_sysdir_guess_programdata_dirs(git_buf
*out
)
24 return git_win32__find_programdata_dirs(out
);
31 static int git_sysdir_guess_system_dirs(git_buf
*out
)
34 return git_win32__find_system_dirs(out
, L
"etc\\");
36 return git_buf_sets(out
, "/etc");
41 static int get_passwd_home(git_buf
*out
, uid_t uid
)
43 struct passwd pwd
, *pwdptr
;
50 if ((buflen
= sysconf(_SC_GETPW_R_SIZE_MAX
)) == -1)
54 buf
= git__realloc(buf
, buflen
);
55 error
= getpwuid_r(uid
, &pwd
, buf
, buflen
, &pwdptr
);
57 } while (error
== ERANGE
&& buflen
<= 8192);
60 git_error_set(GIT_ERROR_OS
, "failed to get passwd entry");
65 git_error_set(GIT_ERROR_OS
, "no passwd entry found for user");
69 if ((error
= git_buf_puts(out
, pwdptr
->pw_dir
)) < 0)
78 static int git_sysdir_guess_global_dirs(git_buf
*out
)
81 return git_win32__find_global_dirs(out
);
85 const char *sandbox_id
;
91 * If APP_SANDBOX_CONTAINER_ID is set, we are running in a
92 * sandboxed environment on macOS.
94 sandbox_id
= getenv("APP_SANDBOX_CONTAINER_ID");
97 * In case we are running setuid, use the configuration
98 * of the effective user.
100 * If we are running in a sandboxed environment on macOS,
101 * we have to get the HOME dir from the password entry file.
103 if (!sandbox_id
&& uid
== euid
)
104 error
= git__getenv(out
, "HOME");
106 error
= get_passwd_home(out
, euid
);
108 if (error
== GIT_ENOTFOUND
) {
117 static int git_sysdir_guess_xdg_dirs(git_buf
*out
)
120 return git_win32__find_xdg_dirs(out
);
122 git_buf env
= GIT_BUF_INIT
;
130 * In case we are running setuid, only look up passwd
131 * directory of the effective user.
134 if ((error
= git__getenv(&env
, "XDG_CONFIG_HOME")) == 0)
135 error
= git_buf_joinpath(out
, env
.ptr
, "git");
137 if (error
== GIT_ENOTFOUND
&& (error
= git__getenv(&env
, "HOME")) == 0)
138 error
= git_buf_joinpath(out
, env
.ptr
, ".config/git");
140 if ((error
= get_passwd_home(&env
, euid
)) == 0)
141 error
= git_buf_joinpath(out
, env
.ptr
, ".config/git");
144 if (error
== GIT_ENOTFOUND
) {
149 git_buf_dispose(&env
);
154 static int git_sysdir_guess_template_dirs(git_buf
*out
)
157 return git_win32__find_system_dirs(out
, L
"share\\git-core\\templates");
159 return git_buf_sets(out
, "/usr/share/git-core/templates");
163 struct git_sysdir__dir
{
165 int (*guess
)(git_buf
*out
);
168 static struct git_sysdir__dir git_sysdir__dirs
[] = {
169 { GIT_BUF_INIT
, git_sysdir_guess_system_dirs
},
170 { GIT_BUF_INIT
, git_sysdir_guess_global_dirs
},
171 { GIT_BUF_INIT
, git_sysdir_guess_xdg_dirs
},
172 { GIT_BUF_INIT
, git_sysdir_guess_programdata_dirs
},
173 { GIT_BUF_INIT
, git_sysdir_guess_template_dirs
},
176 static void git_sysdir_global_shutdown(void)
180 for (i
= 0; i
< ARRAY_SIZE(git_sysdir__dirs
); ++i
)
181 git_buf_dispose(&git_sysdir__dirs
[i
].buf
);
184 int git_sysdir_global_init(void)
189 for (i
= 0; !error
&& i
< ARRAY_SIZE(git_sysdir__dirs
); i
++)
190 error
= git_sysdir__dirs
[i
].guess(&git_sysdir__dirs
[i
].buf
);
192 git__on_shutdown(git_sysdir_global_shutdown
);
197 static int git_sysdir_check_selector(git_sysdir_t which
)
199 if (which
< ARRAY_SIZE(git_sysdir__dirs
))
202 git_error_set(GIT_ERROR_INVALID
, "config directory selector out of range");
207 int git_sysdir_get(const git_buf
**out
, git_sysdir_t which
)
213 GIT_ERROR_CHECK_ERROR(git_sysdir_check_selector(which
));
215 *out
= &git_sysdir__dirs
[which
].buf
;
219 int git_sysdir_get_str(
224 const git_buf
*path
= NULL
;
226 GIT_ERROR_CHECK_ERROR(git_sysdir_check_selector(which
));
227 GIT_ERROR_CHECK_ERROR(git_sysdir_get(&path
, which
));
229 if (!out
|| path
->size
>= outlen
) {
230 git_error_set(GIT_ERROR_NOMEMORY
, "buffer is too short for the path");
234 git_buf_copy_cstr(out
, outlen
, path
);
238 #define PATH_MAGIC "$PATH"
240 int git_sysdir_set(git_sysdir_t which
, const char *search_path
)
242 const char *expand_path
= NULL
;
243 git_buf merge
= GIT_BUF_INIT
;
245 GIT_ERROR_CHECK_ERROR(git_sysdir_check_selector(which
));
247 if (search_path
!= NULL
)
248 expand_path
= strstr(search_path
, PATH_MAGIC
);
250 /* reset the default if this path has been cleared */
252 git_sysdir__dirs
[which
].guess(&git_sysdir__dirs
[which
].buf
);
254 /* if $PATH is not referenced, then just set the path */
257 git_buf_sets(&git_sysdir__dirs
[which
].buf
, search_path
);
262 /* otherwise set to join(before $PATH, old value, after $PATH) */
263 if (expand_path
> search_path
)
264 git_buf_set(&merge
, search_path
, expand_path
- search_path
);
266 if (git_buf_len(&git_sysdir__dirs
[which
].buf
))
267 git_buf_join(&merge
, GIT_PATH_LIST_SEPARATOR
,
268 merge
.ptr
, git_sysdir__dirs
[which
].buf
.ptr
);
270 expand_path
+= strlen(PATH_MAGIC
);
272 git_buf_join(&merge
, GIT_PATH_LIST_SEPARATOR
, merge
.ptr
, expand_path
);
274 git_buf_swap(&git_sysdir__dirs
[which
].buf
, &merge
);
275 git_buf_dispose(&merge
);
278 if (git_buf_oom(&git_sysdir__dirs
[which
].buf
))
284 static int git_sysdir_find_in_dirlist(
291 const char *scan
, *next
= NULL
;
292 const git_buf
*syspath
;
294 GIT_ERROR_CHECK_ERROR(git_sysdir_get(&syspath
, which
));
295 if (!syspath
|| !git_buf_len(syspath
))
298 for (scan
= git_buf_cstr(syspath
); scan
; scan
= next
) {
299 /* find unescaped separator or end of string */
300 for (next
= scan
; *next
; ++next
) {
301 if (*next
== GIT_PATH_LIST_SEPARATOR
&&
302 (next
<= scan
|| next
[-1] != '\\'))
306 len
= (size_t)(next
- scan
);
307 next
= (*next
? next
+ 1 : NULL
);
311 GIT_ERROR_CHECK_ERROR(git_buf_set(path
, scan
, len
));
313 GIT_ERROR_CHECK_ERROR(git_buf_joinpath(path
, path
->ptr
, name
));
315 if (git_path_exists(path
->ptr
))
320 git_buf_dispose(path
);
321 git_error_set(GIT_ERROR_OS
, "the %s file '%s' doesn't exist", label
, name
);
322 return GIT_ENOTFOUND
;
325 int git_sysdir_find_system_file(git_buf
*path
, const char *filename
)
327 return git_sysdir_find_in_dirlist(
328 path
, filename
, GIT_SYSDIR_SYSTEM
, "system");
331 int git_sysdir_find_global_file(git_buf
*path
, const char *filename
)
333 return git_sysdir_find_in_dirlist(
334 path
, filename
, GIT_SYSDIR_GLOBAL
, "global");
337 int git_sysdir_find_xdg_file(git_buf
*path
, const char *filename
)
339 return git_sysdir_find_in_dirlist(
340 path
, filename
, GIT_SYSDIR_XDG
, "global/xdg");
343 int git_sysdir_find_programdata_file(git_buf
*path
, const char *filename
)
345 return git_sysdir_find_in_dirlist(
346 path
, filename
, GIT_SYSDIR_PROGRAMDATA
, "ProgramData");
349 int git_sysdir_find_template_dir(git_buf
*path
)
351 return git_sysdir_find_in_dirlist(
352 path
, NULL
, GIT_SYSDIR_TEMPLATE
, "template");
355 int git_sysdir_expand_global_file(git_buf
*path
, const char *filename
)
359 if ((error
= git_sysdir_find_global_file(path
, NULL
)) == 0) {
361 error
= git_buf_joinpath(path
, path
->ptr
, filename
);